Showing posts with label J2EE. Show all posts
Showing posts with label J2EE. Show all posts

12/03/2013

[MongoDB-Escape dots '.' in map key] Resolve org.springframework.data.mapping.model.MappingException: Map key foo.map.key contains dots but no replacement was configured!

Sometimes we need to save map into MongoDB. But the key of the map cannot have dots inside its keys.

If the key has dot(s), by default we'll get this kind of exception:
org.springframework.data.mapping.model.MappingException: Map key foo.bar.key contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.potentiallyEscapeMapKey(MappingMongoConverter.java:622)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeMapInternal(MappingMongoConverter.java:586)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createMap(MappingMongoConverter.java:517)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:424)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:451)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:451)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:386)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$3.doWithPersistentProperty(MappingMongoConverter.java:373)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:257)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:373)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:345)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:310)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.write(MappingMongoConverter.java:77)
    at org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:859)
    at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:806)
    at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:794)


Refer to the source code:

Spring => "MappingMongoConverter.java"
/**
     * Potentially replaces dots in the given map key with the configured map key replacement if configured or aborts
     * conversion if none is configured.
     * 
     * @see #setMapKeyDotReplacement(String)
     * @param source
     * @return
     */
    protected String potentiallyEscapeMapKey(String source) {

        if (!source.contains(".")) {
            return source;
        }

        if (mapKeyDotReplacement == null) {
            throw new MappingException(String.format("Map key %s contains dots but no replacement was configured! Make "
                    + "sure map keys don't contain dots in the first place or configure an appropriate replacement!", source));
        }

        return source.replaceAll("\\.", mapKeyDotReplacement);
    }

So the solution is configure the property mapKeyDotReplacement for bean MappingMongoConverter in the spring config file.

For example:
<bean id="mongoMoxydomainConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
        <constructor-arg index="0" ref="mongoDbFactory" />
        <constructor-arg index="1">
            <bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
        </constructor-arg>
        <property name="mapKeyDotReplacement" value="\\+"/>
</bean>

What needs to be mentioned is that, the value that we set for "mapKeyDotReplacement" must follow the regular pattern's rule. If use reserved character, must use '\\' to translate it.




8/06/2013

JAXB Marshall and UnMarshall Example (Include post request to REST API)

Pom.xml:
<dependency>
 <groupId>javax.xml.bind</groupId>
 <artifactId>jaxb-api</artifactId>
 <version>2.1</version>
</dependency>
<dependency>
 <groupId>com.sun.xml.bind</groupId>
 <artifactId>jaxb-impl</artifactId>
 <version>2.1</version>
</dependency>

Code:

public final String MESSAGE_URL = "http://test.com/messageservice/send";

public boolean sendMessage(Message message) {
 
       URL url = null;
       try {
           url = new URL(SS_URL);
       } catch (MalformedURLException e1) {
           e1.printStackTrace();
       }

       try {

           System.out.println("Start converting message to XML.");

           // Marshall
       
           JAXBContext jaxbContext = JAXBContext.newInstance(Message.class);
           Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
 
           // output pretty printed
           jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUTtrue);
 
           ByteOutputStream out = new ByteOutputStream();
           jaxbMarshaller.marshal(message, out);

           String msg = new String(out.getBytes(),"utf-8");

           System.out.println("Finish converting message to XML.");

           System.out.println(msg);
         
           HttpURLConnection connect = (HttpURLConnection) url.openConnection();
           connect.setRequestMethod("POST");
           connect.setDoOutput(true);
           connect.setRequestProperty("Content-Type""text/xml");
           connect.setAllowUserInteraction(false);
         
           // send query

           OutputStream os = connect.getOutputStream();
           jaxbMarshaller.marshal(message, os);
           os.flush();
           os.close();

           if (connect.getResponseCode() != 200) {
               System.out.println("Message sent failed.");
               return false;
           }

           System.out.println("Message sent.");

           System.out.println("Result:");
         
           // Unmarshall
       
             Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
           Message result = (N2NMessage) jaxbUnmarshaller.unmarshal(connect.getInputStream());
           System.out.println("Sent Message Id: " + result.getMessageID());
         
         
           return true;
         
       } catch (Exception e) {
           e.printStackTrace();
           return false;
       }
}

6/07/2013

CentralCache Management(Memcache) Issue 1 -- net.spy.memcached.internal.CheckedOperationTimeoutException


One main aspect to evaluate one web app is its scalability, whether it can sustain heavy load.
So when we decide to build one web app, each technology that we plan to use must go through enough tests, not only functional test, but also load test. Otherwise, we may lose at the start point.

When I test the performance of tomcat-memcache central session management, I save&get small session data and large session data with both light load and heavy load.

When load is light, everything goes fine. But when load increase, and session data is around 10K, exception will be thrown:

net.spy.memcached.internal.CheckedOperationTimeoutException: Timed out waiting for operation - failing node: localhost/127.0.0.1:11212
        at net.spy.memcached.internal.OperationFuture.get(OperationFuture.java:160)
        at de.javakaffee.web.msm.LockingStrategy.onAfterBackupSession(LockingStrategy.java:294)
        at de.javakaffee.web.msm.MemcachedSessionService.backupSession(MemcachedSessionService.java:1062)
        at de.javakaffee.web.msm.RequestTrackingHostValve.backupSession(RequestTrackingHostValve.java:243)
        at de.javakaffee.web.msm.RequestTrackingHostValve.invoke(RequestTrackingHostValve.java:168)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1770)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:662)

Regarding this issue, I found one discussion page:
https://code.google.com/p/spymemcached/issues/detail?id=136
But I did not see effect solution. Maybe switch to other memcache client lib will be a better solution.
Somebody says that increase the spymemcache's timeout may help, but I haven't got time to test it.
Compared to memcache, now I prefer mongodb as the backup of session. At least it's client lib is more stable and under enough test.

So If we need to setup the central cache for web app, we need to customize and test the lib well based on our requirement. I think no one wants to face this issue in production.

5/31/2013

Central Session Management (Tomcat, Memcache/MongoDB, Non-Sticky Session)

For all application server clusters, there are two elementary issues that need to be resolved:

  1. How to dispatch requests to the cluster nodes.
  2. Use one session management strategy, to ensure that if one node fails, other nodes can still obtained the session data, to achieve fault-tolerant cluster (failover)

For the first issue, the easiest way is to use uniform hash function to dispatch the request to each node. But for application server, session is used. To make sure one session’s request is properly handled, normally we dispatch the requests related to one session to same node to process, this is “Sticky Session” load balance. And the way randomly dispatch request is called “non-sticky session” load balance.

The “sticky session” sometimes restricts the load balance and even the web app’s design and development. To avoid “sticky session”, we need to find appropriate session management strategy.

There are several options for session share.

Session Share Options

Tomcat Clustering

It simply copied session to all the tomcat instances. If there are many instances, this may cause a lot network traffic. 

Save session related data to database

Actually some apps choose this way to make session management under control.

There are two ways to do this,

  1.  Customize 'session' in the app. In this case, "session" is not HTTP session any more, it just means data that used as session data. 

    Each Tomcat instances can access the central db to get the session related data and load the data into its own cache/session. Also each instances can write data into db for session share purposes.

    Each request need to call the db for twice, one get session, one update session.
    Good point: data is fully under control.
    Bad point: database needs to be periodically clean up.
  2. Extends Tomcat Manager class and customize your own session manager, backup your tomcat session data into MongoDB.

    Reference implementation:
    https://github.com/dawsonsystems/Mongo-Tomcat-Sessions
    https://github.com/dwelch2344/mongo-tomcat-session-manager

    Personally I like this way, because MongoDB is a mature product and in additional, its client lib(driver) is also robust. And it's fast. Although the speed is not that fast compared to backup method like memcache which backup session in memory, but it's still fast enough and the data is more safe. But still, old topic, all the decision should be made based on our own requirement.

    Little Note: In this way, we do not need extra periodical job to clear the session in db. We can override the backgroundProcess() method inheritted from Tomcat's Manager class.

Session in Cookie

This way is to save session data in the client side instead of server side. Each time client make the request, send the session back to server. In this way, developers can make control on session consume. But known issues is security concern. 

Central Cache (External Cache)

External Cache is a separate service that runs on the machine in the LAN. Use external cache can realize central session management. 
It stores the session in memory and all the tomcat instances can access the central cache to read session and set session. There is only one copy of for each session and all the tomcat instances share the same one.

Normally, there should be at least one central cache node and one backup cache node.

There are some implementations for central cache,  memcached (open-source) and Terracotta (open-source, commercial). Central cache has been proved to be fast and reliable, and many large web sites use central cache for session management.

But to set up central cache, may need professional system engineer to setup environment, maintain and monitor.

Summary

The core idea of using Central Cache and MongoDB is actually same, just backup session into one central place for reference. The only difference is cache saves data in memory and db saves data in hard drive.

We can pick one way based on requirement. Both ways have reference implementation on tomcat session manager.


Central Session Mangement - MongoDB Test Case

To test the central cache, I setup the environment and build one test web app.

Structure:


Environment:
Tomcat 7,
Java Default Serializer,
mongodb-win32-x86_64-2.0.7 (Windows 64 bits version)


#Start mongodb
1. mongobin \>mongod.exe --dbpath=”DATA_PATH”

2. In another console start MongoDB client console by executing mongo.exe then execute the following commands.
   use tomcatsessions
   db.addUser("sessionadmin", "sessionadmin234")

3. Stop the MongoDB server (i.e. press Ctrl+C)

4. When the prompt shows, restart the MongoDB server with the --auth parameter:
   mongobin\>mongod.exe  --auth  --dbpath=”DATA_PATH”
 
#Start two tomcat instances

1. Run tomcat-instance\apache-tomcat-7.0.29-1\bin\startup.bat
   Port: 8080
1. Run tomcat-instance\apache-tomcat-7.0.29-2\bin\startup.bat
   Port: 9080
   
Test Case:

#Put String in the session

Access:
 http://localhost:8080/centralcache-test/SessionPage1

 1. Get the session, if not exist, create a new one.
 2. Add attribute "currentUser": "marym" into session

 Page will show:

 Current session Id: ${session id}

 CurrentUser: marym

Then Access:
 http://localhost:9080/centralcache-test/SessionPage2

 1. Get the session, if not exist, create a new one.
 2. Print attribute "currentUser" in the session.

 Page will show:

 Current session Id: ${session id}

 CurrentUser: marym

 
#Put User Object in the session

User{username,password}

Access:
 http://localhost:8080/centralcache-test/login
 http://localhost:9080/centralcache-test/login
 
 If user has not logged in, it will direct to login page. 
 If user has logged in (session has valid attribute "currentUser"), it will display the logged in user's username.  
 If user logout, the "currentUser" in session will be removed. (Not destroy the whole session)
 
Two tomcats share the session. If shut down the first memcache node at port 11211, 
the backup node at port 11212 will be used. Session will not be lost.

Note: Different client side apps do not share session. E.g. IE, Chrome, FireFox do not share session.

#Test Put 10K around session data into session
 http://localhost:8080/testlargeusersession
 http://localhost:9080/testlargeusersession

#Test Put 100bytes around session data into session
 http://localhost:8080/testsmallusersession
 http://localhost:9080/testsmallusersession
 
#Test Multi-thread Support
Open Jmeter Test Case.
Run.

I test 10000 concurrent threads with session data around 10K. Works pretty good.

Central Cache Test Case

To test the central cache, I setup the environment and build one test web app.



Structure:



Environment:
Tomcat 7,
memcached-session-manager-1.6.4
memcached-amd64 (Windows 64 bits version)


#Start two memcache nodes

1. Run memcached-amd64\run-instance1.bat
   Port: 11211
2. Run memcached-amd64\run-instance2.bat
   Port: 11212

#Start two tomcat instances

1. Run tomcat-instance\apache-tomcat-7.0.29-1\bin\startup.bat
   Port: 8080
1. Run tomcat-instance\apache-tomcat-7.0.29-2\bin\startup.bat
   Port: 9080
   
Test Case:

#Put String in the session

Access:
 http://localhost:8080/centralcache-test/SessionPage1

 1. Get the session, if not exist, create a new one.
 2. Add attribute "currentUser": "marym" into session

 Page will show:

 Current session Id: ${session id}

 CurrentUser: marym

Then Access:
 http://localhost:9080/centralcache-test/SessionPage2

 1. Get the session, if not exist, create a new one.
 2. Print attribute "currentUser" in the session.

 Page will show:

 Current session Id: ${session id}

 CurrentUser: marym

#Put User Object in the session

User{username,password}

Access:
 http://localhost:8080/centralcache-test/login
 http://localhost:9080/centralcache-test/login
 If user has not logged in, it will direct to login page. 
 If user has logged in (session has valid attribute "currentUser"), it will display the logged in user's username.  
 If user logout, the "currentUser" in session will be removed. (Not destroy the whole session)
Two tomcats share the session. If shut down the first memcache node at port 11211, 
the backup node at port 11212 will be used. Session will not be lost.

Note: Different client side apps do not share session. E.g. IE, Chrome, FireFox do not share session.


Sample configuration:

1. Add dependencies (jars that should be placed under tomcat\lib):
    
    For Tomcat 7
  • memcached-session-manager-1.6.4.jar
  • memcached-session-manager-tc7-1.6.4.jar
  • spymemcached-2.8.12.jar
  • couchbase-client-1.1.2.jar
    For Tomcat 6
  • memcached-session-manager-1.6.4.jar
  • memcached-session-manager-tc6-1.6.4.jar
  • spymemcached-2.8.12.jar
  • couchbase-client-1.1.2.jar
    
    I've put all these dependencies in the zip file central-session-test.zip

2. Tomcat /conf/context.xml  (All instances use the same conf, non-sticky session)

    #Use Javolution Serializer

<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
   memcachedNodes="n1:localhost:11212,n2:localhost:11213"
   sticky="false"
   sessionBackupAsync="false"
   lockingMode="uriPattern:/path1|/path2"
   requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
   transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
/>

      Extra Dependencies(jars that should be placed under tomcat\lib):

  • javolution-5.4.3.1.jar
  • msm-javolution-serializer-1.6.4.jar

 
 #Use Kryo Serializer

<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
   memcachedNodes="n1:localhost:11212,n2:localhost:11213"
   sticky="false"
   sessionBackupAsync="false"
   lockingMode="uriPattern:/path1|/path2"
   requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
   transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>

      Extra Dependencies(jars that should be placed under tomcat\lib):
  • msm-kryo-serializer-1.6.4.jar
  • kryo-1.04.jar
  • kryo-serializers-0.10.jar
  • asm-3.2.jar
  • reflectasm-1.01.jar
  • minlog-1.2.jar
     Configure Kryo's buffer size.
      By default the initial buffer size is 100K, and maxium is 2M.
      If the session data is big, then we need to update these two values by adding one system property.

      In Tomcat's startup.bat file:

      set JAVA_OPTS=%JAVA_OPTS% -Dmsm.kryo.buffersize.initial=10444800 -Dmsm.kryo.buffersize.max=104448000

3. Start MemCache

Unzip memcached-amd64.zip, Go to memcached-amd64 directory.
memcached -p 11211 -u memcached -m 64 -M -vv
memcached -p 11212 -u memcached -m 64 -M -vv

-vv tells memcached to write lots of stuff to console, so you'll see when a session is requested or stored in the output of memcached.

4. Start Tomcat
    ->Tomcat Base/bin/start.bat




Friendly Reminder:

Please take care on the version number of extra lib that added into Tomcat's library. There are many difference versions of jar. If use wrong version, it may cause jar conflict or class missing.

In my above example. I use memcached-session-manager-1.6.4, then the extra dependency jar files that I listed is based on version 1.6.4 of  memcached-session-manager. If use other different version of memcached-session-manager, please check the dependencies' version and adjust if needed.


Little Notes:

=====Issue 1=====

If you wanna to install the Memcached for Windows and the follow instruction in Memcached on Windows and telnet interface, it will throw exception. "Failed to ignore SIGHUP: Result too large". To resolve this, can follow below steps:

1) run CMD as an administrator
2) type SC create memcached binpath= "c:\memcached\145\memcached.exe -m 512
-d"
3) type NET START memcahed

Although you get an error, the program started (check task manager or connect to it using telnet).

=====Issue 2=====

If you configure like this:

Tomcat context.xml:
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
   memcachedNodes="n1:localhost:11212"
   sticky="false"
   sessionBackupAsync="false"
   lockingMode="uriPattern:/path1|/path2"
   requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
   transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
/>

Error:

java.lang.NoSuchMethodError: net.spy.memcached.MemcachedClient.set(Ljava/lang/String;ILjava/lang/Object;)Lnet/spy/memcached/internal/OperationFuture;
at de.javakaffee.web.msm.BackupSessionTask.storeSessionInMemcached(BackupSessionTask.java:227)
at de.javakaffee.web.msm.BackupSessionTask.doBackupSession(BackupSessionTask.java:194)
at de.javakaffee.web.msm.BackupSessionTask.call(BackupSessionTask.java:119)
at de.javakaffee.web.msm.BackupSessionTask.call(BackupSessionTask.java:50)
at de.javakaffee.web.msm.BackupSessionService$SynchronousExecutorService.submit(BackupSessionService.java:346)
at de.javakaffee.web.msm.BackupSessionService.backupSession(BackupSessionService.java:205)
at de.javakaffee.web.msm.MemcachedSessionService.backupSession(MemcachedSessionService.java:1059)
at de.javakaffee.web.msm.RequestTrackingHostValve.backupSession(RequestTrackingHostValve.java:229)
at de.javakaffee.web.msm.RequestTrackingHostValve.invoke(RequestTrackingHostValve.java:154)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:619)

Solution:

Update memcached jar to spymemcached-2.8.12.jar or spymemcached-2.8.4.jar

=====Issue 3=====

memcached-session-manager provides several serializers. The testing of this part will be processed later, applicability, efficiency, performance, etc.




5/21/2013

Trust Chain Testing of Direct Project

Trust Chain that created:

Test Case 1:
Add subdepart.testhosp.com Cert(signed by Test Hospital CA) to test.com’s trust anchor.
subdepart.testhosp.com and test.com can talk to each other successfully.


Test Case 2:
Add Test Hospital CA(signed by Mock CA) to test.com’s trust anchor.
subdepart.testhosp.com and test.com can talk to each other successfully.

Test Case 3:
Add Mock CA (self-signed) to test.com’s trust anchor.
subdepart.testhosp.com and test.com can talk to each other successfully.

NOTICE:
1.     In the JAVA reference implementation, direct project by default support max trust chain length 5(hard coded).
2.     Each intermediate CA must have cn attribute same as their domain.  For example, in test case 2, the issuer CA of subdepart.testhosp.com is Test Hospital CA, and the issuer CA of Test Hospital CA is mockCA.com. So if subdepart.testhosp.com s issue CA(Test Hospital CA) is not in the trust list, then Test Hospital CA must have CN set to testhosp.com  otherwise, direct will not know where to retrieve the certificate of testhosp.com.

5/09/2013

CRUD for LDAP (UnboundID JDK, ApacheDS)

A raw implementation.
---Two ways to create, use CreateRequest or @LDAPObject
---Retrieve all sub entries or single entry based on DN. If want to retrieve all the entries including sub-entries under one dn, pass in the dn, and set filter as 'objectClass=*'.
---Support recursively delete all entries including sub entries.
     ApacheDS does not implement the tree delete function, so the SubtreeDeleteRequestControl in the jdk is not useful. So to support tree delete for ApacheDS, we need to implement recursively delete ourselves.
---ApacheDS seems does not support ModifyDSRequest with deleteOldRDN set to true.(Old RDN will still be left over, but as attribute instead of rdn, rdn is updated.)

TestService.java



package test.common.ldap.service;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.naming.NamingException;

import test.common.ldap.entity.User;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

import com.unboundid.ldap.sdk.AddRequest;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DeleteRequest;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.LDAPSearchException;
import com.unboundid.ldap.sdk.ModifyDNRequest;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl;
import com.unboundid.ldap.sdk.persist.LDAPPersistException;
import com.unboundid.ldap.sdk.persist.LDAPPersister;
import com.unboundid.ldif.LDIFException;

public class TestService {

   private String host;
   private String portString;
   private int port;
   private String psw;
   private int OPERATION_TIMEOUT_MILLIS = 1000;

   static {

       Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
   }

   public TestService() {

       // String confPath = "..\\conf";
       // String name = confPath + "\\conf.properties";

       String name = "C:\\Projects\\LDAPTestTool\\conf\\conf.properties";

       InputStream in = null;
       try {
           in = new BufferedInputStream(new FileInputStream(name));
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       }
       Properties p = new Properties();
       try {
           p.load(in);
       } catch (IOException e) {
           e.printStackTrace();
       }

       host = p.getProperty("host");
       System.out.println("host read from propery file.");

       portString = p.getProperty("port");
       System.out.println("port read from propery file.");
       port = Integer.parseInt(portString);

       psw = p.getProperty("password");
       System.out.println("password read from propery file.");

   }

   public void create(String entryDN, String cn, byte[] p12Cert,
           byte[] publicKey, byte[] privateKey) {

       byte[] encodedPrivateCert = Base64.encodeBase64(privateKey);
       byte[] encodedP12Cert = Base64.encodeBase64(p12Cert);

       String[] ldifLines = { "dn: " + "cn=" + cn + "," + entryDN,
               "objectClass: top""objectClass: tlsKeyInfo",
               "objectClass: person""objectClass: organizationalPerson",
               "objectClass: inetOrgPerson""changetype: add""cn: " + cn,
               "sn: " + cn, "mail: " + cn, "keyAlgorithm: RSA",
               // "privateKey: " + new String(encodedp12Cert),
               "privateKeyFormat: PKCS#8",
               // "publicKey: " + new String(publicKey),
               "publicKeyFormat: X.509",
       // "userCertificate: " + new String(publicKey),
       };

       LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
       connectionOptions.setAbandonOnTimeout(true);
       connectionOptions.setConnectTimeoutMillis(OPERATION_TIMEOUT_MILLIS);

       int result;
       LDAPResult ldapResult = null;
       try {

           // Connect to the server.
           LDAPConnection ldapConnection = new LDAPConnection(
                   connectionOptions, host, port);
           try {

               // Create the AddRequest object using the LDIF lines.
               AddRequest addRequest = new AddRequest(ldifLines);

               addRequest.addAttribute("privateKey", encodedPrivateCert);
               addRequest.addAttribute("publicKey", publicKey);
               addRequest.addAttribute("userCertificate", publicKey);
               addRequest.addAttribute("userPKCS12", encodedP12Cert);

               // Transmit the AddRequest to the server.
               ldapResult = ldapConnection.add(addRequest);

               System.out.println(ldapResult);

           } catch (final LDIFException e) {
               System.err.println(e);
           } finally {
               ldapConnection.close();

               // Convert the result code to an integer for use in the exit
               // method.
               result = ldapResult == null ? 1 : ldapResult.getResultCode()
                       .intValue();
           }
       } catch (final LDAPException e) {
           System.err.println(e);
           result = 1;
       }

       // System.exit(result);

   }

   public void createWithPersistObject(String entryDN, String cn,
           byte[] p12Cert, byte[] publicKey, byte[] privateKey) {

       byte[] encodedPrivateCert = Base64.encodeBase64(privateKey);
       byte[] encodedP12Cert = Base64.encodeBase64(p12Cert);

       LDAPPersister<User> persister = null;
       try {
           persister = LDAPPersister.getInstance(User.class);
       } catch (LDAPPersistException e2) {
           // TODO Auto-generated catch block
           e2.printStackTrace();
       }

       // Create a new MyObject instance and add it to the directory. We can
       // use
       // a parent DN of null to indicate that it should use the default
       // defined
       // in the @LDAPObject annotation.
       LDAPConnection ldapConnection = null;
       try {
           ldapConnection = getLdapConnection();
       } catch (LDAPException e1) {
           // TODO Auto-generated catch block
           e1.printStackTrace();
       }

       User user = new User();
       user.setDn("cn=" + cn + "," + entryDN);
       user.setCn(cn);
       user.setSn(cn);
       user.setMail(cn);
       user.setPrivateKey(encodedPrivateCert);
       user.setPublicKey(publicKey);
       user.setUserCertificate(publicKey);
       user.setUserPKCS12(encodedP12Cert);

       LDAPResult ldapResult = null;
       int result;
       try {
           ldapResult = persister.add(user, ldapConnection, null);

           System.out.println(ldapResult);

       } catch (Exception e) {
           System.err.println(e);
       } finally {
           ldapConnection.close();

           // Convert the result code to an integer for use in the exit method.
           result = ldapResult == null ? 1 : ldapResult.getResultCode()
                   .intValue();
       }

       // System.exit(result);

   }

   @SuppressWarnings({ "unchecked""rawtypes" })
   public void search(String dn, String psw, String filter)
           throws NamingException {

       LDAPConnection ldapConnection = null;
       try {
           ldapConnection = getAuthLdapConnection(psw);
       } catch (LDAPException e1) {
           // TODO Auto-generated catch block
           e1.printStackTrace();
       }

       if (StringUtils.isNotEmpty(filter)) {
           SearchRequest searchRequest = null;
           try {
               searchRequest = new SearchRequest(dn, SearchScope.SUB, filter);
           } catch (LDAPException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }

           System.out
                   .println("========================================================");
           System.out.println("Search Result: ");

           try {
               SearchResult searchResult = ldapConnection
                       .search(searchRequest);

               for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                   // String name = entry.getAttributeValue("cn");
                   // String mail = entry.getAttributeValue("mail");
                   /*
                    * StringBuilder buffer = new StringBuilder();
                    * 
                    * entry.toString(buffer);
                    * System.out.println(buffer.toString());
                    */
                   System.out.println();
                   System.out.println("Entry DN: " + entry.getDN());
                   List<Attribute> attributes = new ArrayList(
                           entry.getAttributes());
                   System.out
                           .println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
                   System.out.println("Entry content: ");

                   for (Attribute att : attributes) {

                       System.out.print(att.getName() + ": ");

                       String[] values = att.getValues();

                       for (int i = 0; i < values.length - 1; i++) {
                           System.out.print(values[i]);
                           System.out.print(",");
                       }
                       System.out.print(values[values.length - 1]);

                       System.out.println();

                   }
               }
               System.out
                       .println("========================================================");
               System.out.println("Search end.");
           } catch (LDAPSearchException lse) {
               System.err.println("The search failed.");
           }
       } else {

           Entry groupEntry = null;
           try {
               groupEntry = ldapConnection.getEntry(dn);
           } catch (LDAPException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }

           List<Attribute> attributes = new ArrayList(
                   groupEntry.getAttributes());
           System.out
                   .println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
           System.out.println("Entry content: ");

           for (Attribute att : attributes) {

               System.out.print(att.getName() + ": ");

               String[] values = att.getValues();

               for (int i = 0; i < values.length - 1; i++) {
                   System.out.print(values[i]);
                   System.out.print(",");
               }
               System.out.print(values[values.length - 1]);

               System.out.println();

           }

           System.out.println("End Search.");

           /*
            * String[] memberValues = groupEntry.getAttributeValues("member");
            * 
            * if (memberValues != null)
            * 
            * {
            * 
            * DNEntrySource entrySource = new DNEntrySource(ldapConnection,
            * memberValues, "cn");
            * 
            * while (true) { Entry memberEntry = entrySource.nextEntry(); if
            * (memberEntry == null) { break; }
            * 
            * System.out.println("Retrieved member entry:  " +
            * memberEntry.getAttributeValue("cn")); } }
            */
       }

   }

   public void update(String psw, String entryDN, String cn, String newDn,
           String newCn, byte[] p12Cert, byte[] publicKey, byte[] privateKey) {

       LDAPConnection ldapConnection = null;
       String fullDn = "cn=" + cn + "," + entryDN;

       if (StringUtils.isNotEmpty(newDn) || StringUtils.isNotEmpty(newCn)) {

           ModifyDNRequest modifyDNRequest = null;

           if (StringUtils.isEmpty(newDn)) {
               newDn = entryDN;
               modifyDNRequest = new ModifyDNRequest(fullDn, "cn=" + newCn,
                       true);
           }
           if (StringUtils.isEmpty(newCn)) {
               newCn = cn;
               modifyDNRequest = new ModifyDNRequest(fullDn, "cn=" + newCn,
                       true, newDn);
           }
           if (StringUtils.isNotEmpty(newDn) && StringUtils.isNotEmpty(newCn)) {

               modifyDNRequest = new ModifyDNRequest(fullDn, "cn=" + newCn,
                       true, newDn);
           }

           fullDn = "cn=" + newCn + "," + newDn;

           try {
               ldapConnection = null;
               try {
                   ldapConnection = getAuthLdapConnection(psw);
               } catch (LDAPException e1) {
                   // TODO Auto-generated catch block
                   e1.printStackTrace();
               }
               modifyDNRequest.setDeleteOldRDN(true);
               LDAPResult modifyDNResult = ldapConnection
                       .modifyDN(modifyDNRequest);

               System.out.println("The entry was renamed successfully.");
           } catch (LDAPException le) {
               le.printStackTrace();
               System.err.println("The modify DN operation failed.");
           }
       }

       if (StringUtils.isNotEmpty(newCn) || p12Cert != null
               || publicKey != null || privateKey != null) {

           LDAPPersister<User> persister = null;
           try {
               persister = LDAPPersister.getInstance(User.class);
           } catch (LDAPPersistException e2) {
               // TODO Auto-generated catch block
               e2.printStackTrace();
           }

           // Create a new MyObject instance and add it to the directory. We
           // can
           // use
           // a parent DN of null to indicate that it should use the default
           // defined
           // in the @LDAPObject annotation.

           try {
               ldapConnection = getAuthLdapConnection(psw);
           } catch (LDAPException e1) {
               // TODO Auto-generated catch block
               e1.printStackTrace();
           }

           User user = new User();
           user.setDn(fullDn);
           if (StringUtils.isEmpty(newCn)) {
               user.setCn(cn);
               user.setSn(cn);
               user.setMail(cn);
           } else {
               user.setCn(newCn);
               user.setSn(newCn);
               user.setMail(newCn);
           }

           if (p12Cert != null) {
               byte[] encodedP12Cert = Base64.encodeBase64(p12Cert);
               user.setUserPKCS12(encodedP12Cert);
           }
           if (privateKey != null) {
               byte[] encodedPrivateCert = Base64.encodeBase64(privateKey);
               user.setPrivateKey(encodedPrivateCert);
           }

           if (publicKey != null) {
               user.setPublicKey(publicKey);
               user.setUserCertificate(publicKey);
           }

           LDAPResult ldapResult = null;
           int result;
           try {
               ldapResult = persister
                       .modify(user, ldapConnection, nullfalse);

               System.out.println(ldapResult);

           } catch (Exception e) {
               System.err.println(e);
           } finally {
               ldapConnection.close();

               // Convert the result code to an integer for use in the exit
               // method.
               result = ldapResult == null ? 1 : ldapResult.getResultCode()
                       .intValue();
           }
       }

       // System.exit(result);

   }

   public void deleteOneLevel(String psw, String entryDN) {

       LDAPConnection ldapConnection = null;
       try {
           ldapConnection = getAuthLdapConnection(psw);
       } catch (LDAPException e1) {
           // TODO Auto-generated catch block
           e1.printStackTrace();
       }

       DeleteRequest deleteRequest = new DeleteRequest(entryDN);
       deleteRequest.addControl(new SubtreeDeleteRequestControl());

       LDAPResult ldapResult = null;
       int result;
       try {
           ldapResult = ldapConnection.delete(deleteRequest);

           System.out.println("The entry was successfully deleted.");
       } catch (LDAPException le) {
           le.printStackTrace();
           System.err.println("The delete operation failed.");
       } finally {
           ldapConnection.close();

           // Convert the result code to an integer for use in the exit method.
           result = ldapResult == null ? 1 : ldapResult.getResultCode()
                   .intValue();
       }

       // System.exit(result);
   }

   public void delete(String psw, String entryDN) throws LDAPException {

       LDAPConnection ldapConnection = null;
       try {
           ldapConnection = getAuthLdapConnection(psw);
       } catch (LDAPException e1) {
           // TODO Auto-generated catch block
           e1.printStackTrace();
       }

       deleteSubEntry(entryDN, ldapConnection);

       return;
   }

   public void deleteSubEntry(String entry, LDAPConnection ldapConnection)
           throws LDAPException {

       // System.out.println("deleteSubEntry '"+entry+"' start.");

       SearchRequest searchRequest = null;
       String filter = "objectClass=*";

       searchRequest = new SearchRequest(entry, SearchScope.ONE, filter);

       SearchResult searchResult = null;

       // System.out.println("search sub-entry for entry '"+entry+"' start.");
       searchResult = ldapConnection.search(searchRequest);

       if (searchResult.getEntryCount() == 0) {
           // System.out.println("No sub entry.");
           deleteEntry(entry, ldapConnection);
       } else {
           // System.out.println("Sub entries exist.");
           for (SearchResultEntry entryResult : searchResult
                   .getSearchEntries()) {

               if (!entryResult.getDN().equalsIgnoreCase(entry)) {
                   // System.out.println("Get sub entry: " +
                   // entryResult.getDN());
                   deleteSubEntry(entryResult.getDN(), ldapConnection);
               }

           }
           // System.out.println("Start delete parent entry " + entry);
           deleteEntry(entry, ldapConnection);
       }

       // return;
   }

   public void deleteEntry(String entry, LDAPConnection ldapConnection) {

       DeleteRequest deleteRequest = new DeleteRequest(entry);

       LDAPResult ldapResult = null;
       int result;
       try {
           ldapResult = ldapConnection.delete(deleteRequest);

           System.out.println("The entry '" + entry
                   + "' was successfully deleted.");
       } catch (LDAPException le) {
           le.printStackTrace();
           System.err.println("The delete operation for entry '" + entry
                   + "' failed.");
       } finally {

           // Convert the result code to an integer for use in the exit method.
           result = ldapResult == null ? 1 : ldapResult.getResultCode()
                   .intValue();
       }
   }

   public LDAPConnection getLdapConnection() throws LDAPException {

       LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
       connectionOptions.setAbandonOnTimeout(true);
       connectionOptions.setConnectTimeoutMillis(OPERATION_TIMEOUT_MILLIS);

       LDAPConnection ldapConnection = new LDAPConnection(connectionOptions,
               host, port);

       return ldapConnection;
   }

   public LDAPConnection getAuthLdapConnection(String psw)
           throws LDAPException {

       LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
       connectionOptions.setAbandonOnTimeout(true);
       connectionOptions.setConnectTimeoutMillis(OPERATION_TIMEOUT_MILLIS);

       LDAPConnection ldapConnection = new LDAPConnection(connectionOptions,
               host, port, "uid=admin,ou=system", psw);

       return ldapConnection;
   }

   public byte[] readDataFromFile(String path) {

       InputStream is = null;
       ByteArrayOutputStream out = new ByteArrayOutputStream();

       try {
           is = new BufferedInputStream(new FileInputStream(path));
           byte[] b = new byte[1024];
           int n;
           while ((n = is.read(b)) != -1) {
               out.write(b, 0, n);
           }

       } catch (Exception e) {
           // e.printStackTrace();
           return null;
       } finally {
           if (is != null) {
               try {
                   is.close();
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       }
       return out.toByteArray();
   }

   private byte[] InputStreamToByte(InputStream is) throws IOException {

       ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
       int ch;
       while ((ch = is.read()) != -1) {
           bytestream.write(ch);
       }
       byte imgdata[] = bytestream.toByteArray();
       bytestream.close();
       return imgdata;

   }
}



App.java

package test.common.ldap.tool;

import java.util.Scanner;

import javax.naming.NamingException;

import test.common.ldap.service.TestService;

import org.apache.commons.lang.StringUtils;

import com.unboundid.ldap.sdk.LDAPException;

/**
*
*/
public class App
{
   public static void main( String[] args )
   {  
       TestService service = new TestService();
       manu(service);
   }
 
   public static void manu(TestService service){
       System.out.println("----------------------------------------");
       System.out.println("----------------MODE--------------------");
       System.out.println("1: create");
       System.out.println("2: retrieve");
       System.out.println("3: update");
       System.out.println("4: delete");
     
       Scanner input = new Scanner(System.in);
       System.out.print("Key in the selection : ");
     
       String selectionString = input.next();
     
       boolean isValid = StringUtils.isNumeric(selectionString);
       if(isValid) {
           int option = Integer.parseInt(selectionString);
           if(option==1){
             
               System.out.print("Please input the entry dn: ");
               Scanner inputScanner = new Scanner(System.in);
               String dn = inputScanner.next();
               //String dn = "ou=users,ou=system";
               System.out.println("entry dn: " + dn);
             
               System.out.print("Please input the entry cn: ");
               inputScanner = new Scanner(System.in);
               String cn = inputScanner.next();
             
               System.out.println("entry cn: " + cn);
             
               System.out.print("Please input the p12 cert location: ");
               byte[] p12certData = getCertDataFromPath(service);
             
               System.out.println("p12 cert data read: " + new String(p12certData) );
             
             
             
               System.out.print("Please input the public cert location: ");
               byte[] publicCertData =getCertDataFromPath(service);
             
               System.out.println("public cert data read: " + new String(publicCertData));
             
             
             
               System.out.print("Please input the private cert location: ");
               byte[] privateCertData = getCertDataFromPath(service);
             
               System.out.println("private cert data read: " + new String(privateCertData));
             
             
             
               service.createWithPersistObject(dn, cn, p12certData, publicCertData, privateCertData);
           }else if(option ==2){
             
               System.out.print("Please input the entry dn: ");
               Scanner inputScanner = new Scanner(System.in);
               String dn = inputScanner.next();
               //String dn = "ou=system";
               System.out.println("entry dn: " + dn);
             
               System.out.print("Please input the password: ");
               inputScanner = new Scanner(System.in);
               String psw = inputScanner.next();
             
               System.out.println("password: " + psw);
             
               System.out.print("Please input the filter(if do not have, input n): ");
               inputScanner = new Scanner(System.in);
               String filter = inputScanner.next();
             
               if(filter.equalsIgnoreCase("n")){
                   filter = null;
               }else{
                   System.out.println("entry filter: " + filter);
               }
             
             
               try {
                   service.search(dn, psw, filter);
               } catch (NamingException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
               }
             
           }else if(option ==3){
             
               System.out.print("Please input the entry dn: ");
               Scanner inputScanner = new Scanner(System.in);
               String dn = inputScanner.next();
               //String dn = "ou=users,ou=system";
               System.out.println("entry dn: " + dn);
             
               System.out.print("Please input the password: ");
               inputScanner = new Scanner(System.in);
               String psw = inputScanner.next();
             
               System.out.println("password: " + psw);
             
               System.out.print("Please input the entry cn: ");
               inputScanner = new Scanner(System.in);
               String cn = inputScanner.next();
             
               System.out.println("entry cn: " + cn);
             
               System.out.print("Please input the new entry dn(if do not want to update, input n): ");
               inputScanner = new Scanner(System.in);
               String newDn = inputScanner.next();
               //String newDn = "ou=system";
               if(newDn.equalsIgnoreCase("n")){
                   newDn = null;
               }else{
                   System.out.println("New entry dn: " + newDn);
               }
             
               System.out.print("Please input the new cn(if do not want to update, input n): ");
               inputScanner = new Scanner(System.in);
               String newCn = inputScanner.next();
             
               if(newCn.equalsIgnoreCase("n")){
                   newCn = null;
               }else{
                   System.out.println("New cn: " + newCn);
               }
             
             
               System.out.println("For below options, if no update, please input n or any other invalid path");

               System.out.print("Please input the p12 cert location: ");
               inputScanner = new Scanner(System.in);
               String certLocation = inputScanner.next();
               byte[] p12certData = getCertData(service,certLocation);
               if(p12certData!=null){
                   System.out.println("p12 cert data read: " + new String(p12certData) );
               }
             
             
             
             
               System.out.print("Please input the public cert location: ");
               inputScanner = new Scanner(System.in);
               certLocation = inputScanner.next();
               byte[] publicCertData = getCertData(service,certLocation);

               if(publicCertData!=null){
                   System.out.println("public cert data read: " + new String(publicCertData));
               }
             
               System.out.print("Please input the private cert location: ");
               inputScanner = new Scanner(System.in);
               certLocation = inputScanner.next();
               byte[] privateCertData = getCertData(service,certLocation);

               if(privateCertData!=null){
                   System.out.println("private cert data read: " + new String(privateCertData));
               }
             
             

               service.update(psw, dn, cn, newDn, newCn, p12certData, publicCertData, privateCertData);
             
           }else if(option ==4){
               System.out.println("WARN: DELETE will delete all the sub-entries too, if exist.");
               System.out.print("Please input the entry dn: ");
               Scanner inputScanner = new Scanner(System.in);
               String dn = inputScanner.next();

               System.out.println("entry dn: " + dn);
             
               System.out.print("Please input the password: ");
               inputScanner = new Scanner(System.in);
               String psw = inputScanner.next();

               System.out.println("password: " + psw);
             
               try {
                   service.delete(psw, dn);
               } catch (LDAPException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
               }
             
           }else{
               System.out.println("Invalid option: " + selectionString);
               manu(service);
           }
       } else {
           System.out.println("Invalid option: " + selectionString);
           manu(service);
       }
     
       manu(service);
   }
 
   public static byte[] getCertDataFromPath(TestService service){
     
       Scanner inputScanner = new Scanner(System.in);
       String certLocation = inputScanner.next();
     
       byte[] data = null;
       try{
           data = service.readDataFromFile(certLocation);
         
       }catch(Exception e){
           e.printStackTrace();
           data = null;
       }
       if(data==null){
           System.out.println("Invalid cert path: " + certLocation);
           System.out.print("Please enter again: ");
           return getCertDataFromPath(service);
       }
     
       return data;
   }

   static byte[] getCertData(TestService service, String certLocation){
     
       byte[] data = null;
       try{
           data = service.readDataFromFile(certLocation);
       }catch(Exception e){
           e.printStackTrace();
           data = null;
       }
       return data;
   }
 
 
}


User.java

package test.common.ldap.entity;

import com.unboundid.ldap.sdk.persist.FilterUsage;
import com.unboundid.ldap.sdk.persist.LDAPDNField;
import com.unboundid.ldap.sdk.persist.LDAPField;
import com.unboundid.ldap.sdk.persist.LDAPObject;

@LDAPObject(structuralClass = "inetOrgPerson", auxiliaryClass = "tlsKeyInfo", defaultParentDN = "ou=users,ou=system")
public class User {

   @LDAPDNField
   private String dn;
 
   // The field used for RDN attribute myStringAttr.
   @LDAPField(attribute = "cn", inRDN = true, filterUsage = FilterUsage.ALWAYS_ALLOWED, requiredForEncode = true)
   private String cn;

   @LDAPField(attribute = "sn")
   private String sn;
 
   @LDAPField(attribute = "mail")
   private String mail;

   @LDAPField(attribute = "keyAlgorithm")
   private String keyAlgorithm = "RSA";

   @LDAPField(attribute = "privateKeyFormat")
   private String privateKeyFormat = "PKCS#8";

   @LDAPField(attribute = "publicKeyFormat")
   private String publicKeyFormat = "X.509";

   @LDAPField(attribute = "privateKey")
   private byte[] privateKey;

   @LDAPField(attribute = "publicKey")
   private byte[] publicKey;

   @LDAPField(attribute = "userCertificate")
   private byte[] userCertificate;

   @LDAPField(attribute = "userPKCS12")
   private byte[] userPKCS12;

 
   public String getDn() {
       return dn;
   }

   public void setDn(String dn) {
       this.dn = dn;
   }
 
   public String getCn() {
       return cn;
   }

   public void setCn(String cn) {
       this.cn = cn;
   }

   public String getSn() {
       return sn;
   }

   public void setSn(String sn) {
       this.sn = sn;
   }

   public String getMail() {
       return mail;
   }

   public void setMail(String mail) {
       this.mail = mail;
   }

   public String getKeyAlgorithm() {
       return keyAlgorithm;
   }

   public void setKeyAlgorithm(String keyAlgorithm) {
       this.keyAlgorithm = keyAlgorithm;
   }

   public String getPrivateKeyFormat() {
       return privateKeyFormat;
   }

   public void setPrivateKeyFormat(String privateKeyFormat) {
       this.privateKeyFormat = privateKeyFormat;
   }

   public String getPublicKeyFormat() {
       return publicKeyFormat;
   }

   public void setPublicKeyFormat(String publicKeyFormat) {
       this.publicKeyFormat = publicKeyFormat;
   }

   public byte[] getPrivateKey() {
       return privateKey;
   }

   public void setPrivateKey(byte[] privateKey) {
       this.privateKey = privateKey;
   }

   public byte[] getPublicKey() {
       return publicKey;
   }

   public void setPublicKey(byte[] publicKey) {
       this.publicKey = publicKey;
   }

   public byte[] getUserCertificate() {
       return userCertificate;
   }

   public void setUserCertificate(byte[] userCertificate) {
       this.userCertificate = userCertificate;
   }

   public byte[] getUserPKCS12() {
       return userPKCS12;
   }

   public void setUserPKCS12(byte[] userPKCS12) {
       this.userPKCS12 = userPKCS12;
   }
}