You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by sm...@apache.org on 2014/10/22 17:44:55 UTC

[36/51] [partial] Rename packages from org.openldap.fortress to org.apache.directory.fortress.core. Change default suffix to org.apache. Switch default ldap api from unbound to apache ldap.

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java b/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java
new file mode 100644
index 0000000..8a499be
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java
@@ -0,0 +1,1384 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
+import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
+import org.apache.directory.api.ldap.codec.standalone.StandaloneLdapApiService;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
+import org.apache.directory.api.ldap.model.exception.LdapReferralException;
+import org.apache.directory.api.ldap.model.message.CompareRequest;
+import org.apache.directory.api.ldap.model.message.CompareRequestImpl;
+import org.apache.directory.api.ldap.model.message.CompareResponse;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionConfig;
+import org.apache.directory.ldap.client.api.LdapConnectionPool;
+import org.apache.directory.ldap.client.api.PoolableLdapConnectionFactory;
+
+import org.apache.directory.fortress.core.CfgRuntimeException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.cfg.Config;
+import org.apache.directory.fortress.core.rbac.FortEntity;
+import org.apache.directory.fortress.core.rbac.Hier;
+import org.apache.directory.fortress.core.rbac.Relationship;
+import org.apache.directory.fortress.core.util.attr.VUtil;
+import org.apache.directory.fortress.core.util.crypto.EncryptUtil;
+import org.apache.directory.fortress.core.util.time.CUtil;
+import org.apache.directory.fortress.core.util.time.Constraint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Abstract class contains methods to perform low-level entity to ldap persistence.  These methods are called by the
+ * Fortress DAO's, i.e. {@link org.apache.directory.fortress.core.rbac.dao.apache.UserDAO}. {@link org.apache.directory.fortress.core.rbac.dao.apache
+ * .RoleDAO}, {@link org.apache.directory.fortress.core.rbac.dao.apache.PermDAO}, ....
+ * These are low-level data utilities, very little if any data validations are performed here.
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author Shawn McKinney
+ */
+public abstract class ApacheDsDataProvider
+{
+    // Logging
+    private static final String CLS_NM = ApacheDsDataProvider.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+    private static final int MAX_DEPTH = 100;
+    private static final LdapCounters counters = new LdapCounters();
+    private static final String LDAP_HOST = "host";
+    private static final String LDAP_PORT = "port";
+    private static final String LDAP_ADMIN_POOL_MIN = "min.admin.conn";
+    private static final String LDAP_ADMIN_POOL_MAX = "max.admin.conn";
+    private static final String LDAP_ADMIN_POOL_UID = "admin.user";
+    private static final String LDAP_ADMIN_POOL_PW = "admin.pw";
+
+    // Used for TLS/SSL client-side configs:
+    private static final String ENABLE_LDAP_SSL = "enable.ldap.ssl";
+    private static final String ENABLE_LDAP_SSL_DEBUG = "enable.ldap.ssl.debug";
+    private static final String TRUST_STORE = Config.getProperty( "trust.store" );
+    private static final String TRUST_STORE_PW = Config.getProperty( "trust.store.password" );
+    private static final boolean IS_SSL = (
+        Config.getProperty( ENABLE_LDAP_SSL ) != null   &&
+            Config.getProperty( ENABLE_LDAP_SSL ).equalsIgnoreCase( "true" ) &&
+            TRUST_STORE      != null   &&
+            TRUST_STORE_PW   != null );
+
+    private static final String SET_TRUST_STORE_PROP = "trust.store.set.prop";
+    private static final boolean IS_SET_TRUST_STORE_PROP = (
+        IS_SSL &&
+            Config.getProperty( SET_TRUST_STORE_PROP ) != null   &&
+            Config.getProperty( SET_TRUST_STORE_PROP ).equalsIgnoreCase( "true" ));
+
+    private static final boolean IS_SSL_DEBUG = ( ( Config.getProperty( ENABLE_LDAP_SSL_DEBUG ) != null ) && ( Config
+        .getProperty( ENABLE_LDAP_SSL_DEBUG ).equalsIgnoreCase( "true" ) ) );
+
+    /**
+     * The Admin connection pool
+     */
+    private static LdapConnectionPool adminPool;
+
+    /**
+     * The Log connection pool
+     */
+    private static LdapConnectionPool logPool;
+
+    /**
+     * The User connection pool
+     */
+    private static LdapConnectionPool userPool;
+
+    static
+    {
+        String host = Config.getProperty( LDAP_HOST, "localhost" );
+        int port = Config.getInt( LDAP_PORT, 10389 );
+        int min = Config.getInt( LDAP_ADMIN_POOL_MIN, 1 );
+        int max = Config.getInt( LDAP_ADMIN_POOL_MAX, 10 );
+
+        if(IS_SET_TRUST_STORE_PROP)
+        {
+            LOG.info( "Set JSSE truststore properties in Apache LDAP client:");
+            LOG.info( "javax.net.ssl.trustStore: " + TRUST_STORE );
+            LOG.info( "javax.net.debug: " + new Boolean( IS_SSL_DEBUG ).toString());
+            System.setProperty( "javax.net.ssl.trustStore", TRUST_STORE );
+            System.setProperty( "javax.net.ssl.trustStorePassword", TRUST_STORE_PW );
+            System.setProperty( "javax.net.debug", new Boolean( IS_SSL_DEBUG ).toString() );
+        }
+
+        LdapConnectionConfig config = new LdapConnectionConfig();
+        config.setLdapHost( host );
+        config.setLdapPort( port );
+        config.setName( Config.getProperty( LDAP_ADMIN_POOL_UID, "" ) );
+
+        // added by smckinney for TLS/SSL config:
+        config.setUseSsl( IS_SSL );
+        //config.setTrustManagers( new NoVerificationTrustManager() );
+
+        config.setTrustManagers( new LdapClientTrustStoreManager(
+            TRUST_STORE,
+            TRUST_STORE_PW.toCharArray() , null, true ) );
+
+        String adminPw = null;
+
+        if ( EncryptUtil.isEnabled() )
+        {
+            adminPw = EncryptUtil.decrypt( Config.getProperty( LDAP_ADMIN_POOL_PW ) );
+        }
+        else
+        {
+            adminPw = Config.getProperty( LDAP_ADMIN_POOL_PW );
+        }
+
+        config.setCredentials( adminPw );
+        try
+        {
+            System.setProperty( StandaloneLdapApiService.EXTENDED_OPERATIONS_LIST,
+                "org.openldap.accelerator.impl.createSession.RbacCreateSessionFactory,"
+              + "org.openldap.accelerator.impl.checkAccess.RbacCheckAccessFactory,"
+              + "org.openldap.accelerator.impl.addRole.RbacAddRoleFactory,"
+              + "org.openldap.accelerator.impl.dropRole.RbacDropRoleFactory,"
+              + "org.openldap.accelerator.impl.deleteSession.RbacDeleteSessionFactory,"
+              + "org.openldap.accelerator.impl.sessionRoles.RbacSessionRolesFactory"
+                );
+
+            LdapApiService ldapApiService = new StandaloneLdapApiService();
+            if ( LdapApiServiceFactory.isInitialized() == false )
+            {
+                LdapApiServiceFactory.initialize( ldapApiService );
+            }
+            config.setLdapApiService( ldapApiService );
+        }
+        catch ( Exception ex )
+        {
+            String error =  "Exception caught initializing Admin Pool: " + ex;
+            throw new CfgRuntimeException( GlobalErrIds.FT_APACHE_LDAP_POOL_INIT_FAILED, error, ex );
+        }
+
+        PoolableLdapConnectionFactory factory = new PoolableLdapConnectionFactory( config );
+
+        // Create the Admin pool
+        adminPool = new LdapConnectionPool( factory );
+        adminPool.setTestOnBorrow( true );
+        adminPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+        adminPool.setMaxActive( max );
+        adminPool.setMinIdle( min );
+
+        // Create the Log pool
+        logPool = new LdapConnectionPool( factory );
+        logPool.setTestOnBorrow( true );
+        logPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+        logPool.setMaxActive( max );
+        logPool.setMinIdle( min );
+
+        // Create the User pool
+        userPool = new LdapConnectionPool( factory );
+        userPool.setTestOnBorrow( true );
+        userPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+        userPool.setMaxActive( max );
+        userPool.setMinIdle( min );
+    }
+
+
+    /**
+     * Given a contextId and a fortress param name return the LDAP dn.
+     *
+     * @param contextId is to determine what sub-tree to use.
+     * @param root      contains the fortress parameter name that corresponds with a particular LDAP container.
+     * @return String contains the dn to use for operation.
+     */
+    protected String getRootDn( String contextId, String root )
+    {
+        String szDn = Config.getProperty( root );
+        StringBuilder dn = new StringBuilder();
+
+        // The contextId must not be null, or "HOME" or "null"
+        if ( VUtil.isNotNullOrEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) && !contextId
+            .equals( GlobalIds.HOME ) )
+        {
+            int idx = szDn.indexOf( Config.getProperty( GlobalIds.SUFFIX ) );
+
+            if ( idx != -1 )
+            {
+                // Found. The DN is ,ou=<contextId>,  
+                dn.append( szDn.substring( 0, idx - 1 ) ).append( "," ).append( GlobalIds.OU ).append( "=" ).append(
+                    contextId ).append( "," ).append( szDn.substring( idx ) );
+            }
+        }
+        else
+        {
+            dn.append( szDn );
+        }
+
+        return dn.toString();
+    }
+
+
+    /**
+     * Given a contextId return the LDAP dn that includes the suffix.
+     *
+     * @param contextId is to determine what sub-tree to use.
+     * @return String contains the dn to use for operation.
+     */
+    protected String getRootDn( String contextId )
+    {
+        StringBuilder dn = new StringBuilder();
+        if ( VUtil.isNotNullOrEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) && !contextId
+            .equals( GlobalIds.HOME ) )
+        {
+            dn.append( GlobalIds.OU ).append( "=" ).append( contextId ).append( "," +
+                "" ).append( Config.getProperty( GlobalIds.SUFFIX ) );
+        }
+        else
+        {
+            dn.append( Config.getProperty( GlobalIds.SUFFIX ) );
+        }
+        return dn.toString();
+    }
+
+
+    /**
+     * Read the ldap record from specified location.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains ldap distinguished name.
+     * @param attrs      array contains array names to pull back.
+     * @return ldap entry.
+     * @throws LdapException in the event system error occurs.
+     */
+    protected Entry read( LdapConnection connection, String dn, String[] attrs ) throws LdapException
+    {
+        counters.incrementRead();
+
+        return connection.lookup( dn, attrs );
+    }
+
+
+    /**
+     * Read the ldap record from specified location with user assertion.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains ldap distinguished name.
+     * @param attrs      array contains array names to pull back.                                        ,
+     *                   PoolMgr.ConnType.USER
+     * @param userDn     string value represents the identity of user on who's behalf the request was initiated.  The
+     *                   value will be stored in openldap auditsearch record AuthZID's attribute.
+     * @return ldap entry.
+     * @throws LdapException                in the event system error occurs.
+     * @throws UnsupportedEncodingException for search control errors.
+     */
+    protected Entry read( LdapConnection connection, String dn, String[] attrs, String userDn ) throws LdapException
+    {
+        counters.incrementRead();
+
+        return connection.lookup( dn, attrs );
+    }
+
+
+    /**
+     * Add a new ldap entry to the directory.  Do not add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param entry      contains data to add..
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void add( LdapConnection connection, Entry entry ) throws LdapException
+    {
+        counters.incrementAdd();
+        connection.add( entry );
+    }
+
+
+    /**
+     * Add a new ldap entry to the directory.  Add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param entry      contains data to add..
+     * @param entity     contains audit context.
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void add( LdapConnection connection, Entry entry, FortEntity entity ) throws LdapException
+    {
+        counters.incrementAdd();
+
+        if ( GlobalIds.IS_AUDIT && entity != null && entity.getAdminSession() != null )
+        {
+            if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+            {
+                entry.add( GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
+            }
+
+            if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+            {
+                entry.add( GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
+            }
+
+            if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+            {
+                entry.add( GlobalIds.FT_MODIFIER_ID, entity.getModId() );
+            }
+        }
+
+        connection.add( entry );
+    }
+
+
+    /**
+     * Update exiting ldap entry to the directory.  Do not add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry.
+     * @param mods       contains data to modify.
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void modify( LdapConnection connection, String dn, List<Modification> mods ) throws LdapException
+    {
+        counters.incrementMod();
+        connection.modify( dn, mods.toArray( new Modification[]{} ) );
+    }
+
+
+    /**
+     * Update exiting ldap entry to the directory.  Add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry.
+     * @param mods       contains data to modify.
+     * @param entity     contains audit context.
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void modify( LdapConnection connection, String dn, List<Modification> mods,
+        FortEntity entity ) throws LdapException
+    {
+        counters.incrementMod();
+        audit( mods, entity );
+        connection.modify( dn, mods.toArray( new Modification[]{} ) );
+    }
+
+
+    /**
+     * Delete exiting ldap entry from the directory.  Do not add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry targeted for removal..
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void delete( LdapConnection connection, String dn ) throws LdapException
+    {
+        counters.incrementDelete();
+        connection.delete( dn );
+    }
+
+
+    /**
+     * Delete exiting ldap entry from the directory.  Add audit context.  This method will call modify prior to
+     * delete which will
+     * force corresponding audit record to be written to slapd access log.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry targeted for removal..
+     * @param entity     contains audit context.
+     * @throws LdapException in the event system error occurs.
+     */
+    protected void delete( LdapConnection connection, String dn, FortEntity entity ) throws LdapException
+    {
+        counters.incrementDelete();
+        List<Modification> mods = new ArrayList<Modification>();
+        audit( mods, entity );
+
+        if ( mods.size() > 0 )
+        {
+            modify( connection, dn, mods );
+        }
+
+        connection.delete( dn );
+    }
+
+
+    /**
+     * Delete exiting ldap entry and all descendants from the directory.  Do not add audit context.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry targeted for removal..
+     * @throws LdapException   in the event system error occurs.
+     * @throws IOException
+     * @throws CursorException
+     */
+    protected void deleteRecursive( LdapConnection connection, String dn ) throws LdapException, CursorException
+    {
+        int recursiveCount = 0;
+        deleteRecursive( dn, connection, recursiveCount );
+    }
+
+
+    /**
+     * Delete exiting ldap entry and all descendants from the directory.  Add audit context.  This method will call
+     * modify prior to delete which will
+     * force corresponding audit record to be written to slapd access log.
+     *
+     * @param connection handle to ldap connection.
+     * @param dn         contains distinguished node of entry targeted for removal..
+     * @param entity     contains audit context.
+     * @throws LdapException   in the event system error occurs.
+     * @throws IOException
+     * @throws CursorException
+     */
+    protected void deleteRecursive( LdapConnection connection, String dn, FortEntity entity ) throws LdapException,
+        CursorException
+    {
+        List<Modification> mods = new ArrayList<Modification>();
+        audit( mods, entity );
+
+        if ( mods.size() > 0 )
+        {
+            modify( connection, dn, mods );
+        }
+
+        deleteRecursive( connection, dn );
+    }
+
+
+    /**
+     * Used to recursively remove all nodes up to record pointed to by dn attribute.
+     *
+     * @param dn             contains distinguished node of entry targeted for removal..
+     * @param connection     handle to ldap connection.
+     * @param recursiveCount keeps track of how many iterations have been performed.
+     * @throws LdapException   in the event system error occurs.
+     * @throws IOException
+     * @throws CursorException
+     */
+    private void deleteRecursive( String dn, LdapConnection connection, int recursiveCount ) throws LdapException,
+        CursorException
+    {
+        String method = "deleteRecursive";
+
+        // Sanity check - only allow max tree depth of 100
+        if ( recursiveCount++ > MAX_DEPTH )
+        {
+            // too deep inside of a recursive sequence;
+            String error = "." + method + " dn [" + dn + "] depth error in recursive";
+            throw new LdapOperationErrorException( error );
+        }
+
+        String theDN;
+
+        // Find child nodes
+        SearchCursor cursor = search( connection, dn, SearchScope.ONELEVEL, "(objectclass=*)", GlobalIds.NO_ATRS,
+            false, 0 );
+
+        // Iterate over all entries under this entry
+        while ( cursor.next() )
+        {
+            try
+            {
+                // Next directory entry
+                Entry entry = cursor.getEntry();
+                theDN = entry.getDn().getName();
+                // continue down:
+                deleteRecursive( theDN, connection, recursiveCount );
+                recursiveCount--;
+            }
+            catch ( LdapReferralException lre )
+            {
+                // cannot continue;
+                String error = "." + method + " dn [" + dn + "] caught LDAPReferralException=" + lre.getMessage() +
+                    "=" + lre.getReferralInfo();
+                throw lre;
+            }
+            catch ( LdapException le )
+            {
+                // cannot continue;
+                String error = "." + method + " dn [" + dn + "] caught LdapException=" + le.getMessage();
+                throw new LdapException( error );
+            }
+        }
+
+        // delete the node:
+        counters.incrementDelete();
+        delete( connection, dn );
+    }
+
+
+    /**
+     * Add the audit context variables to the modfication set.
+     *
+     * @param mods   used to update ldap attributes.
+     * @param entity contains audit context.
+     * @throws LdapException in the event of error with ldap client.
+     */
+    private void audit( List<Modification> mods, FortEntity entity )
+    {
+        if ( GlobalIds.IS_AUDIT && ( entity != null ) && ( entity.getAdminSession() != null ) )
+        {
+            if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+            {
+                Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                    GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
+                mods.add( modification );
+            }
+
+            if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+            {
+                Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                    GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
+                mods.add( modification );
+            }
+
+            if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+            {
+                Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                    GlobalIds.FT_MODIFIER_ID, entity.getModId() );
+                mods.add( modification );
+            }
+        }
+    }
+
+
+    /**
+     * Perform normal ldap search accepting default batch size.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param baseDn     contains address of distinguished name to begin ldap search
+     * @param scope      indicates depth of search starting at basedn.  0 (base dn),
+     *                   1 (one level down) or 2 (infinite) are valid values.
+     * @param filter     contains the search criteria
+     * @param attrs      is the requested list of attritubutes to return from directory search.
+     * @param attrsOnly  if true pull back attribute names only.
+     * @return result set containing ldap entries returned from directory.
+     * @throws LdapException thrown in the event of error in ldap client or server code.
+     */
+    protected SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+        String[] attrs, boolean attrsOnly ) throws LdapException
+    {
+        counters.incrementSearch();
+
+        SearchRequest searchRequest = new SearchRequestImpl();
+        searchRequest.setBase( new Dn( baseDn ) );
+        searchRequest.setScope( scope );
+        searchRequest.setFilter( filter );
+        searchRequest.setTypesOnly( attrsOnly );
+        searchRequest.addAttributes( attrs );
+        SearchCursor result = connection.search( searchRequest );
+
+        return result;
+    }
+
+
+    /**
+     * Perform normal ldap search specifying default batch size.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param baseDn     contains address of distinguished name to begin ldap search
+     * @param scope      indicates depth of search starting at basedn.  0 (base dn),
+     *                   1 (one level down) or 2 (infinite) are valid values.
+     * @param filter     contains the search criteria
+     * @param attrs      is the requested list of attributes to return from directory search.
+     * @param attrsOnly  if true pull back attribute names only.
+     * @param batchSize  Will block until this many entries are ready to return from server.  0 indicates to block
+     *                   until all results are ready.
+     * @return result set containing ldap entries returned from directory.
+     * @throws LdapException thrown in the event of error in ldap client or server code.
+     */
+    protected SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+        String[] attrs, boolean attrsOnly, int batchSize ) throws LdapException
+    {
+        counters.incrementSearch();
+
+        SearchRequest searchRequest = new SearchRequestImpl();
+
+        searchRequest.setBase( new Dn( baseDn ) );
+        searchRequest.setFilter( filter );
+        searchRequest.setScope( scope );
+        searchRequest.setSizeLimit( batchSize );
+        searchRequest.setTypesOnly( attrsOnly );
+        searchRequest.addAttributes( attrs );
+
+        SearchCursor result = connection.search( searchRequest );
+
+        return result;
+    }
+
+
+    /**
+     * Perform normal ldap search specifying default batch size and max entries to return.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param baseDn     contains address of distinguished name to begin ldap search
+     * @param scope      indicates depth of search starting at basedn.  0 (base dn),
+     *                   1 (one level down) or 2 (infinite) are valid values.
+     * @param filter     contains the search criteria
+     * @param attrs      is the requested list of attritubutes to return from directory search.
+     * @param attrsOnly  if true pull back attribute names only.
+     * @param batchSize  Will block until this many entries are ready to return from server.  0 indicates to block
+     *                   until all results are ready.
+     * @param maxEntries specifies the maximum number of entries to return in this search query.
+     * @return result set containing ldap entries returned from directory.
+     * @throws LdapException thrown in the event of error in ldap client or server code.
+     */
+    protected SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+        String[] attrs, boolean attrsOnly, int batchSize, int maxEntries ) throws LdapException
+    {
+        counters.incrementSearch();
+
+        SearchRequest searchRequest = new SearchRequestImpl();
+
+        searchRequest.setBase( new Dn( baseDn ) );
+        searchRequest.setFilter( filter );
+        searchRequest.setScope( scope );
+        searchRequest.setSizeLimit( batchSize );
+        searchRequest.setTypesOnly( attrsOnly );
+        searchRequest.addAttributes( attrs );
+
+        SearchCursor result = connection.search( searchRequest );
+
+        return result;
+    }
+
+
+    /**
+     * This method will search the directory and return at most one record.  If more than one record is found
+     * an ldap exception will be thrown.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param baseDn     contains address of distinguished name to begin ldap search
+     * @param scope      indicates depth of search starting at basedn.  0 (base dn),
+     *                   1 (one level down) or 2 (infinite) are valid values.
+     * @param filter     contains the search criteria
+     * @param attrs      is the requested list of attritubutes to return from directory search.
+     * @param attrsOnly  if true pull back attribute names only.
+     * @return entry   containing target ldap node.
+     * @throws LdapException   thrown in the event of error in ldap client or server code.
+     * @throws IOException
+     * @throws CursorException
+     */
+    protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+        String[] attrs, boolean attrsOnly ) throws LdapException, CursorException, IOException
+    {
+        SearchRequest searchRequest = new SearchRequestImpl();
+
+        searchRequest.setBase( new Dn( baseDn ) );
+        searchRequest.setFilter( filter );
+        searchRequest.setScope( scope );
+        searchRequest.setTypesOnly( attrsOnly );
+        searchRequest.addAttributes( attrs );
+
+        SearchCursor result = connection.search( searchRequest );
+
+        Entry entry = result.getEntry();
+
+        if ( result.next() )
+        {
+            throw new LdapException( "searchNode failed to return unique record for LDAP search of base DN [" +
+                baseDn + "] filter [" + filter + "]" );
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * This search method uses OpenLDAP Proxy Authorization Control to assert arbitrary user identity onto connection.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param baseDn     contains address of distinguished name to begin ldap search
+     * @param scope      indicates depth of search starting at basedn.  0 (base dn),
+     *                   1 (one level down) or 2 (infinite) are valid values.
+     * @param filter     contains the search criteria
+     * @param attrs      is the requested list of attritubutes to return from directory search.
+     * @param attrsOnly  if true pull back attribute names only.
+     * @param userDn     string value represents the identity of user on who's behalf the request was initiated.  The
+     *                   value will be stored in openldap auditsearch record AuthZID's attribute.
+     * @return entry   containing target ldap node.
+     * @throws LdapException   thrown in the event of error in ldap client or server code.
+     * @throws IOException
+     * @throws CursorException
+     */
+    protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+        String[] attrs, boolean attrsOnly, String userDn ) throws LdapException, CursorException, IOException
+    {
+        counters.incrementSearch();
+
+        SearchRequest searchRequest = new SearchRequestImpl();
+
+        searchRequest.setBase( new Dn( baseDn ) );
+        searchRequest.setFilter( filter );
+        searchRequest.setScope( scope );
+        searchRequest.setTypesOnly( attrsOnly );
+        searchRequest.addAttributes( attrs );
+
+        SearchCursor result = connection.search( searchRequest );
+
+        Entry entry = result.getEntry();
+
+        if ( result.next() )
+        {
+            throw new LdapException( "searchNode failed to return unique record for LDAP search of base DN [" +
+                baseDn + "] filter [" + filter + "]" );
+        }
+
+        return entry;
+    }
+
+
+    /**
+     * This method uses the compare ldap func to assert audit record into the directory server's configured audit
+     * logger.
+     *
+     * @param connection is LdapConnection object used for all communication with host.
+     * @param dn         contains address of distinguished name to begin ldap search
+     * @param userDn     dn for user node
+     * @param attribute  attribute used for compare
+     * @return true if compare operation succeeds
+     * @throws LdapException                thrown in the event of error in ldap client or server code.
+     * @throws UnsupportedEncodingException in the event the server cannot perform the operation.
+     */
+    protected boolean compareNode( LdapConnection connection, String dn, String userDn,
+        Attribute attribute ) throws LdapException, UnsupportedEncodingException
+    {
+        counters.incrementCompare();
+
+        CompareRequest compareRequest = new CompareRequestImpl();
+        compareRequest.setName( new Dn( dn ) );
+        compareRequest.setAttributeId( attribute.getId() );
+        compareRequest.setAssertionValue( attribute.getString() );
+
+        CompareResponse response = connection.compare( compareRequest );
+
+        return response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS;
+    }
+
+
+    /**
+     * Method wraps ldap client to return multi-occurring attribute values by name within a given entry and returns
+     * as a list of strings.
+     *
+     * @param entry         contains the target ldap entry.
+     * @param attributeName name of ldap attribute to retrieve.
+     * @return List of type string containing attribute values.
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected List<String> getAttributes( Entry entry, String attributeName )
+    {
+        List<String> attrValues = new ArrayList<>();
+        if ( entry != null )
+        {
+            Attribute attr = entry.get( attributeName );
+            if ( attr != null )
+            {
+                for ( Value<?> value : attr )
+                {
+                    attrValues.add( value.getString() );
+                }
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        return attrValues;
+    }
+
+
+    protected byte[] getPhoto( Entry entry, String attributeName ) throws LdapInvalidAttributeValueException
+    {
+        byte[] photo = null;
+        Attribute attr = entry.get( attributeName );
+
+        if ( attr != null )
+        {
+            photo = attr.getBytes();
+        }
+
+        return photo;
+    }
+
+
+    /**
+     * Method wraps ldap client to return multi-occurring attribute values by name within a given entry and returns
+     * as a set of strings.
+     *
+     * @param entry         contains the target ldap entry.
+     * @param attributeName name of ldap attribute to retrieve.
+     * @return List of type string containing attribute values.
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected Set<String> getAttributeSet( Entry entry, String attributeName )
+    {
+        // create Set with case insensitive comparator:
+        Set<String> attrValues = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+
+        if ( entry != null && entry.containsAttribute( attributeName ) )
+        {
+            for ( Value<?> value : entry.get( attributeName ) )
+            {
+                attrValues.add( value.getString() );
+            }
+        }
+
+        return attrValues;
+    }
+
+
+    /**
+     * Method wraps ldap client to return attribute value by name within a given entry and returns as a string.
+     *
+     * @param entry         contains the target ldap entry.
+     * @param attributeName name of ldap attribute to retrieve.
+     * @return value contained in a string variable.
+     * @throws LdapInvalidAttributeValueException
+     *
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected String getAttribute( Entry entry, String attributeName ) throws LdapInvalidAttributeValueException
+    {
+        if ( entry != null )
+        {
+            Attribute attr = entry.get( attributeName );
+
+            if ( attr != null )
+            {
+                return attr.getString();
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Method will retrieve the relative distinguished name from a distinguished name variable.
+     *
+     * @param dn contains ldap distinguished name.
+     * @return rDn as string.
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected String getRdn( String dn )
+    {
+        try
+        {
+            return new Dn( dn ).getRdn().getName();
+        }
+        catch ( LdapInvalidDnException lide )
+        {
+            return null;
+        }
+    }
+
+
+    /**
+     * Create multi-occurring ldap attribute given array of strings and attribute name.
+     *
+     * @param name   contains attribute name to create.
+     * @param values array of string that contains attribute values.
+     * @return Attribute containing multi-occurring attribute set.
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected Attribute createAttributes( String name, String values[] ) throws LdapException
+    {
+        Attribute attr = new DefaultAttribute( name, values );
+
+        return attr;
+    }
+
+
+    /**
+     * Convert constraint from raw ldap format to application entity.
+     *
+     * @param le         ldap entry containing constraint.
+     * @param ftDateTime reference to {@link org.apache.directory.fortress.util.time.Constraint} containing formatted data.
+     * @throws LdapInvalidAttributeValueException
+     *
+     * @throws LdapException in the event of ldap client error.
+     */
+    protected void unloadTemporal( Entry le, Constraint ftDateTime ) throws LdapInvalidAttributeValueException
+    {
+        String szRawData = getAttribute( le, GlobalIds.CONSTRAINT );
+
+        if ( szRawData != null && szRawData.length() > 0 )
+        {
+            CUtil.setConstraint( szRawData, ftDateTime );
+        }
+    }
+
+
+    /**
+     * Given an ldap attribute name and a list of attribute values, construct an ldap attribute set to be added to directory.
+     *
+     * @param list     list of type string containing attribute values to load into attribute set.
+     * @param entry    contains ldap attribute set targeted for adding.
+     * @param attrName name of ldap attribute being added.
+     */
+    protected void loadAttrs( List<String> list, Entry entry, String attrName ) throws LdapException
+    {
+        if ( list != null && list.size() > 0 )
+        {
+            entry.add( attrName, list.toArray( new String[]{} ) );
+        }
+    }
+
+
+    /**
+     * Given an ldap attribute name and a list of attribute values, construct an ldap modification set to be updated
+     * in directory.
+     *
+     * @param list     list of type string containing attribute values to load into modification set.
+     * @param mods     contains ldap modification set targeted for updating.
+     * @param attrName name of ldap attribute being modified.
+     */
+    protected void loadAttrs( List<String> list, List<Modification> mods, String attrName )
+    {
+        if ( ( list != null ) && ( list.size() > 0 ) )
+        {
+            mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName,
+                list.toArray( new String[]{} ) ) );
+        }
+    }
+
+
+    /**
+     * Given a collection of {@link org.apache.directory.fortress.core.rbac.Relationship}s, convert to raw data name-value format and
+     * load into ldap modification set in preparation for ldap modify.
+     *
+     * @param list     contains List of type {@link org.apache.directory.fortress.core.rbac.Relationship} targeted for updating in ldap.
+     * @param mods     ldap modification set containing parent-child relationships in raw ldap format.
+     * @param attrName contains the name of the ldap attribute to be updated.
+     * @param op       specifies type of mod: {@link Hier.Op#ADD}, {@link org.apache.directory.fortress.core.rbac.Hier.Op#MOD},
+     * {@link Hier.Op#REM}
+     */
+    protected void loadRelationshipAttrs( List<Relationship> list, List<Modification> mods, String attrName,
+        Hier.Op op )
+    {
+        if ( list != null )
+        {
+            Attribute attr;
+
+            for ( Relationship rel : list )
+            {
+                // This LDAP attr is stored as a name-value pair separated by a ':'.
+                attr = new DefaultAttribute( attrName, rel.getChild() + GlobalIds.PROP_SEP + rel.getParent() );
+
+                switch ( op )
+                {
+                    case ADD:
+                        mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attr ) );
+                        break;
+
+                    case MOD:
+                        mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attr ) );
+                        break;
+
+                    case REM:
+                        mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attr ) );
+                        break;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Given an ldap attribute name and a set of attribute values, construct an ldap modification set to be updated
+     * in directory.
+     *
+     * @param values   set of type string containing attribute values to load into modification set.
+     * @param mods     contains ldap modification set targeted for updating.
+     * @param attrName name of ldap attribute being updated.
+     */
+    protected void loadAttrs( Set<String> values, List<Modification> mods, String attrName )
+    {
+        if ( ( values != null ) && ( values.size() > 0 ) )
+        {
+            mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName,
+                values.toArray( new String[]{} ) ) );
+        }
+    }
+
+
+    /**
+     * Given an ldap attribute name and a set of attribute values, construct an ldap attribute set to be added to
+     * directory.
+     *
+     * @param values   set of type string containing attribute values to load into attribute set.
+     * @param entry    contains ldap entry to pull attrs from.
+     * @param attrName name of ldap attribute being added.
+     * @throws LdapException
+     */
+    protected void loadAttrs( Set<String> values, Entry entry, String attrName ) throws LdapException
+    {
+        if ( ( values != null ) && ( values.size() > 0 ) )
+        {
+            entry.add( attrName, values.toArray( new String[]{} ) );
+        }
+    }
+
+
+    /**
+     * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap
+     * modification set in preparation for ldap modify.
+     *
+     * @param props    contains {@link java.util.Properties} targeted for updating in ldap.
+     * @param mods     ldap modification set containing name-value pairs in raw ldap format.
+     * @param attrName contains the name of the ldap attribute to be updated.
+     * @param replace  boolean variable, if set to true use {@link ModificationOperation#REPLACE_ATTRIBUTE} else {@link
+     * ModificationOperation#ADD_ATTRIBUTE}.
+     */
+    protected void loadProperties( Properties props, List<Modification> mods, String attrName, boolean replace )
+    {
+        loadProperties( props, mods, attrName, replace, GlobalIds.PROP_SEP );
+    }
+
+
+    /**
+     * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap
+     * modification set in preparation for ldap modify.
+     *
+     * @param props    contains {@link java.util.Properties} targeted for updating in ldap.
+     * @param mods     ldap modification set containing name-value pairs in raw ldap format.
+     * @param attrName contains the name of the ldap attribute to be updated.
+     * @param replace  boolean variable, if set to true use {@link ModificationOperation#REPLACE_ATTRIBUTE} else {@link
+     * ModificationOperation#ADD_ATTRIBUTE}.
+     * @param separator contains the char value used to separate name and value in ldap raw format.
+     */
+    protected void loadProperties( Properties props, List<Modification> mods, String attrName, boolean replace, char separator )
+    {
+        if ( props != null && props.size() > 0 )
+        {
+            if ( replace )
+            {
+                mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName ) );
+            }
+
+            for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+            {
+                String key = ( String ) e.nextElement();
+                String val = props.getProperty( key );
+                // This LDAP attr is stored as a name-value pair separated by a ':'.
+                mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attrName,
+                    key + separator + val ) );
+            }
+        }
+    }
+
+
+    /**
+     * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap
+     * modification set in preparation for ldap modify.
+     *
+     * @param props    contains {@link java.util.Properties} targeted for removal from ldap.
+     * @param mods     ldap modification set containing name-value pairs in raw ldap format to be removed.
+     * @param attrName contains the name of the ldap attribute to be removed.
+     */
+    protected void removeProperties( Properties props, List<Modification> mods, String attrName )
+    {
+        if ( props != null && props.size() > 0 )
+        {
+            Attribute prop;
+
+            for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+            {
+                String key = ( String ) e.nextElement();
+                String val = props.getProperty( key );
+
+                // This LDAP attr is stored as a name-value pair separated by a ':'.
+                mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attrName,
+                    key + GlobalIds.PROP_SEP + val ) );
+            }
+        }
+    }
+
+
+    /**
+     * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap
+     * modification set in preparation for ldap add.
+     *
+     * @param props    contains {@link java.util.Properties} targeted for adding to ldap.
+     * @param entry    contains ldap entry to pull attrs from.
+     * @param attrName contains the name of the ldap attribute to be added.
+     * @throws LdapException
+     */
+    protected void loadProperties( Properties props, Entry entry, String attrName ) throws LdapException
+    {
+        if ( ( props != null ) && ( props.size() > 0 ) )
+        {
+            Attribute attr = new DefaultAttribute( attrName );
+
+            for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+            {
+                // This LDAP attr is stored as a name-value pair separated by a ':'.
+                String key = ( String ) e.nextElement();
+                String val = props.getProperty( key );
+                String prop = key + GlobalIds.PROP_SEP + val;
+
+                attr.add( prop );
+            }
+
+            if ( attr.size() != 0 )
+            {
+                entry.add( attr );
+            }
+        }
+    }
+
+
+    /**
+     * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap add.
+     *
+     * @param props    contains {@link java.util.Properties} targeted for adding to ldap.
+     * @param entry    contains ldap entry to push attrs into.
+     * @param attrName contains the name of the ldap attribute to be added.
+     * @param separator contains the char value used to separate name and value in ldap raw format.
+     * @throws LdapException
+     */
+    protected void loadProperties( Properties props, Entry entry, String attrName, char separator ) throws LdapException
+    {
+        if ( props != null && props.size() > 0 )
+        {
+            Attribute attr = null;
+            for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+            {
+                // This LDAP attr is stored as a name-value pair separated by a ':'.
+                String key = ( String ) e.nextElement();
+                String val = props.getProperty( key );
+                String prop = key + separator + val;
+                if ( attr == null )
+                {
+                    attr = new DefaultAttribute( attrName );
+                }
+                else
+                {
+                    attr.add( prop );
+                }
+            }
+            if ( attr != null )
+            {
+                entry.add( attr );
+            }
+        }
+    }
+
+
+    /**
+     * @param value
+     * @param validLen
+     * @return String containing encoded data.
+     * @throws LdapException
+     */
+    protected String encodeSafeText( String value, int validLen ) throws LdapException
+    {
+        if ( VUtil.isNotNullOrEmpty( value ) )
+        {
+            int length = value.length();
+            if ( length > validLen )
+            {
+                String error = "encodeSafeText value [" + value + "] invalid length [" + length + "]";
+                throw new LdapException( error );
+            }
+            if ( GlobalIds.LDAP_FILTER_SIZE_FOUND )
+            {
+                value = VUtil.escapeLDAPSearchFilter( value );
+            }
+        }
+        return value;
+    }
+
+
+    /**
+     * Calls the PoolMgr to perform an LDAP bind for a user/password combination.  This function is valid
+     * if and only if the user entity is a member of the USERS data set.  The LDAP directory
+     * will return the OpenLDAP PW Policy control.
+     *
+     * @param connection connection to ldap server.
+     * @param userDn     contains the LDAP dn to the user entry.
+     * @param password   contains the password in clear text.
+     * @return boolean value - true if bind successful, false otherwise.
+     * @throws LdapException in the event of LDAP error.
+     */
+    protected boolean bind( LdapConnection connection, String userDn, char[] password ) throws LdapException
+    {
+        counters.incrementBind();
+
+        connection.bind( userDn, new String( password ) );
+        return true;
+    }
+
+
+    /**
+     * Calls the PoolMgr to close the Admin LDAP connection.
+     *
+     * @param connection handle to ldap connection object.
+     * @throws Exception
+     */
+    protected void closeAdminConnection( LdapConnection connection )
+    {
+        try
+        {
+            adminPool.releaseConnection( connection );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Calls the PoolMgr to close the Log LDAP connection.
+     *
+     * @param connection handle to ldap connection object.
+     * @throws Exception
+     */
+    protected void closeLogConnection( LdapConnection connection )
+    {
+        try
+        {
+            logPool.releaseConnection( connection );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Calls the PoolMgr to close the User LDAP connection.
+     *
+     * @param connection handle to ldap connection object.
+     * @throws Exception
+     */
+    protected void closeUserConnection( LdapConnection connection )
+    {
+        try
+        {
+            userPool.releaseConnection( connection );
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Calls the PoolMgr to get an Admin connection to the LDAP server.
+     *
+     * @return ldap connection.
+     * @throws LdapException
+     */
+    protected LdapConnection getAdminConnection() throws LdapException
+    {
+        try
+        {
+            return adminPool.getConnection();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Calls the PoolMgr to get an Log connection to the LDAP server.
+     *
+     * @return ldap connection.
+     * @throws LdapException
+     */
+    protected LdapConnection getLogConnection() throws LdapException
+    {
+        try
+        {
+            return logPool.getConnection();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Calls the PoolMgr to get an User connection to the LDAP server.
+     *
+     * @return ldap connection.
+     * @throws LdapException
+     */
+    protected LdapConnection getUserConnection() throws LdapException
+    {
+        try
+        {
+            return userPool.getConnection();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapException( e.getMessage() );
+        }
+    }
+
+
+    /**
+     * Return to call reference to dao counter object with running totals for ldap operations add, mod, delete, search, etc.
+     *
+     * @return {@link LdapCounters} contains long values of atomic ldap operations for current running process.
+     */
+    public static LdapCounters getLdapCounters()
+    {
+        return counters;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java b/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java
new file mode 100755
index 0000000..c6dc457
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java
@@ -0,0 +1,664 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+
+import java.security.GeneralSecurityException;
+import java.util.Date;
+
+import com.unboundid.ldap.sdk.migrate.ldapjdk.JavaToLDAPSocketFactory;
+import com.unboundid.util.ssl.SSLUtil;
+import com.unboundid.util.ssl.TrustStoreTrustManager;
+import org.apache.directory.fortress.core.cfg.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPConnection;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPException;
+
+import javax.net.ssl.SSLSocketFactory;
+
+
+/**
+ * This connection pool class is used by Fortress {@link PoolMgr}.
+ * PoolMgr operations utilize multiple instances of this class to connections for different purposes.
+ * For example the 'admin' pool contains connections that have privileges to make modifications to the directory data during administrative operations {@link org.apache.directory.fortress.core.AdminMgr}.
+ * The 'user' pool contain unprivileged connections used for authentication processing only, {@link org.apache.directory.fortress.core.AccessMgr}.
+ * A 3rd pool, may be used to interrogate data stored by OpenLDAP's slapo access log info, This is used interrogating the fortress audit log events, {@link org.apache.directory.fortress.core.AuditMgr}.
+ * The contents of this file have been derived from the original, Mozilla Java LDAP SDK, and are subject to the Netscape Public License Version 1.1 (the "License")
+ * as described at the top of this file;
+ * The code mods include additional functionality to enable SSL connections in pool.  There have been other updates to the original functions to integrate with UnboundID's Java LDAP SDK.
+ * </p>
+ * Original Mozilla javadoc:
+ * Class to maintain a pool of individual connections to the
+ * same server. Specify the initial size and the max size
+ * when constructing a pool. Call getConnection() to obtain
+ * a connection from the pool and close() to return it. If
+ * the pool is fully extended and there are no free connections,
+ * getConnection() blocks until a connection has been returned
+ * to the pool.<BR>
+ * Call destroy() to release all connections.
+ * <BR><BR>Example:<BR>
+ * <PRE>
+ * ConnectionPool pool = null;
+ * try {
+ * pool = new ConnectionPool( 10, 30,
+ * "foo.acme.com",389,
+ * "uid=me, o=acme.com",
+ * "password" );
+ * } catch ( LDAPException e ) {
+ * System.err.println( "Unable to create connection pool" );
+ * System.exit( 1 );
+ * }
+ * while ( clientsKnocking ) {
+ * String filter = getSearchFilter();
+ * LDAPConnection ld = pool.getConnection();
+ * try {
+ * LDAPSearchResults res = ld.search( BASE, ld.SCOPE_SUB,
+ * filter, attrs,
+ * false );
+ * pool.close( ld );
+ * while( res.hasMoreElements() ) {
+ * ...
+ * </PRE>
+ */
+class ConnectionPool
+{
+    // Logging
+    private static final String CLS_NM = ConnectionPool.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+
+    /**
+     * Create a new instance of connection pool with specified parameters.  These connections will be used by the Fortress DAO
+     * methods for processing ldap server operations.
+     *
+     * @param min    initial number of connections
+     * @param max    maximum number of connections
+     * @param host   hostname of LDAP server
+     * @param port   port number of LDAP server
+     * @param authdn DN to authenticate as
+     * @param authpw password for authentication
+     * @throws LDAPException on failure to create connections
+     */
+    ConnectionPool( int min, int max,
+        String host, int port,
+        String authdn, String authpw )
+        throws LDAPException
+    {
+        this( min, max, host, port, authdn, authpw, null );
+    }
+
+
+    /*
+     * Constructor for using an existing connection to clone
+     * from
+     * 
+     * @param min initial number of connections
+     * @param max maximum number of connections
+     * @param host hostname of LDAP server
+     * @param port port number of LDAP server
+     * @param authdn DN to authenticate as
+     * @param authpw password for authentication
+     * @param ldc connection to clone 
+     * @exception LDAPException on failure to create connections 
+     */
+    private ConnectionPool( int min, int max,
+        String host, int port,
+        String authdn, String authpw,
+        LDAPConnection ldc )
+        throws LDAPException
+    {
+        this.poolSize = min;
+        this.poolMax = max;
+        this.host = host;
+        this.port = port;
+        this.authdn = authdn;
+        this.authpw = authpw;
+        this.ldc = ldc;
+        this.debugMode = false;
+        createPool();
+    }
+
+
+    /**
+     * Destroy the whole pool - called during a shutdown
+     */
+    void destroy()
+    {
+        for ( int i = 0; i < pool.size(); i++ )
+        {
+            disconnect( ( LDAPConnectionObject ) pool.elementAt( i ) );
+        }
+        pool.removeAllElements();
+    }
+
+
+    /**
+     * Gets a connection from the pool
+     * <p/>
+     * If no connections are available, the pool will be
+     * extended if the number of connections is less than
+     * the maximum; if the pool cannot be extended, the method
+     * blocks until a free connection becomes available.
+     *
+     * @return an active connection.
+     */
+    LDAPConnection getConnection()
+    {
+        LDAPConnection con;
+
+        while ( ( con = getConnFromPool() ) == null )
+        {
+            synchronized ( pool )
+            {
+                try
+                {
+                    pool.wait();
+                }
+                catch ( InterruptedException e )
+                {
+                    LOG.warn( "getConnection caught InterruptedException" );
+                }
+            }
+        }
+        return con;
+    }
+
+
+    /**
+     * Gets a connection from the pool within a time limit.
+     * <p/>
+     * If no connections are available, the pool will be
+     * extended if the number of connections is less than
+     * the maximum; if the pool cannot be extended, the method
+     * blocks until a free connection becomes available or the
+     * time limit is exceeded.
+     *
+     * @param timeout timeout in milliseconds
+     * @return an active connection or <CODE>null</CODE> if timed out.
+     */
+    LDAPConnection getConnection( int timeout )
+    {
+        LDAPConnection con;
+
+        while ( ( con = getConnFromPool() ) == null )
+        {
+            long t1, t0 = System.currentTimeMillis();
+
+            if ( timeout <= 0 )
+            {
+                return con;
+            }
+
+            synchronized ( pool )
+            {
+                try
+                {
+                    pool.wait( timeout );
+                }
+                catch ( InterruptedException e )
+                {
+                    LOG.warn( "getConnection caught InterruptedException for timeout: " + timeout );
+                    return null;
+                }
+            }
+
+            t1 = System.currentTimeMillis();
+            timeout -= ( t1 - t0 );
+        }
+        return con;
+    }
+
+
+    /**
+     * Gets a connection from the pool
+     * <p/>
+     * If no connections are available, the pool will be
+     * extended if the number of connections is less than
+     * the maximum; if the pool cannot be extended, the method
+     * returns null.
+     *
+     * @return an active connection or null.
+     */
+    synchronized LDAPConnection getConnFromPool()
+    {
+        LDAPConnection con = null;
+        LDAPConnectionObject ldapconnobj = null;
+
+        int pSize = pool.size();
+
+        // Get an available connection
+        for ( int i = 0; i < pSize; i++ )
+        {
+
+            // Get the ConnectionObject from the pool
+            LDAPConnectionObject co =
+                ( LDAPConnectionObject ) pool.elementAt( i );
+
+            if ( co.isAvailable() )
+            { // Conn available?
+                ldapconnobj = co;
+                break;
+            }
+        }
+
+        if ( ldapconnobj == null )
+        {
+            // If there there were no conns in pool, can we grow
+            // the pool?
+            if ( ( poolMax < 0 ) ||
+                ( ( poolMax > 0 ) &&
+                ( pSize < poolMax ) ) )
+            {
+
+                // Yes we can grow it
+                int i = addConnection();
+
+                // If a new connection was created, use it
+                if ( i >= 0 )
+                {
+                    ldapconnobj =
+                        ( LDAPConnectionObject ) pool.elementAt( i );
+                }
+            }
+            else
+            {
+                debug( "All pool connections in use" );
+            }
+        }
+
+        if ( ldapconnobj != null )
+        {
+            ldapconnobj.setInUse( true ); // Mark as in use
+            con = ldapconnobj.getLDAPConn();
+        }
+        return con;
+    }
+
+
+    /**
+     * This is our soft close - all we do is mark
+     * the connection as available for others to use.
+     * We also reset the auth credentials in case
+     * they were changed by the caller.
+     *
+     * @param ld a connection to return to the pool
+     */
+    synchronized void close( LDAPConnection ld )
+    {
+
+        int index = find( ld );
+        if ( index != -1 )
+        {
+            LDAPConnectionObject co =
+                ( LDAPConnectionObject ) pool.elementAt( index );
+            // Reset the auth if necessary
+            if ( ldc == null )
+            {
+                boolean reauth = false;
+                //if user bound anon then getAuthenticationDN is null
+                if ( ld.getAuthenticationDN() == null )
+                {
+                    reauth = ( authdn != null );
+                }
+                else if ( !ld.getAuthenticationDN().equalsIgnoreCase( authdn ) )
+                {
+                    reauth = true;
+                }
+            }
+            co.setInUse( false ); // Mark as available
+            synchronized ( pool )
+            {
+                pool.notifyAll();
+            }
+        }
+    }
+
+
+    /**
+     * Debug method to print the contents of the pool
+     */
+    public void printPool()
+    {
+        System.out.println( "--ConnectionPool--" );
+        for ( int i = 0; i < pool.size(); i++ )
+        {
+            LDAPConnectionObject co =
+                ( LDAPConnectionObject ) pool.elementAt( i );
+            String msg = "" + i + "=" + co;
+            LOG.info( "printPool: " + msg );
+        }
+    }
+
+
+    private void disconnect(
+        LDAPConnectionObject ldapconnObject )
+    {
+        if ( ldapconnObject != null )
+        {
+            if ( ldapconnObject.isAvailable() )
+            {
+                LDAPConnection ld = ldapconnObject.getLDAPConn();
+                if ( ( ld != null ) && ( ld.isConnected() ) )
+                {
+                    try
+                    {
+                        ld.disconnect();
+                    }
+                    catch ( LDAPException e )
+                    {
+                        debug( "disconnect: " + e.toString() );
+                        LOG.warn( "disconnect caught LDAPException: " + e.getMessage() );
+                    }
+                }
+                ldapconnObject.setLDAPConn( null ); // Clear conn
+            }
+        }
+    }
+
+
+    private void createPool() throws LDAPException
+    {
+        // Called by the constructors
+        if ( poolSize <= 0 )
+        {
+            throw new LDAPException( "ConnectionPoolSize invalid" );
+        }
+        if ( poolMax < poolSize )
+        {
+            debug( "ConnectionPoolMax is invalid, set to " +
+                poolSize );
+            poolMax = poolSize;
+        }
+
+        debug( "****Initializing LDAP Pool****" );
+        debug( "LDAP host = " + host + " on port " + port );
+        debug( "Number of connections=" + poolSize );
+        debug( "Maximum number of connections=" + poolMax );
+        debug( "******" );
+
+        pool = new java.util.Vector(); // Create pool vector
+        setUpPool( poolSize ); // Initialize it
+    }
+
+
+    private int addConnection()
+    {
+        int index = -1;
+
+        debug( "adding a connection to pool..." );
+        try
+        {
+            int size = pool.size() + 1; // Add one connection
+            setUpPool( size );
+
+            if ( size == pool.size() )
+            {
+                // New size is size requested?
+                index = size - 1;
+            }
+        }
+        catch ( Exception ex )
+        {
+            debug( "Adding a connection: " + ex.toString() );
+            LOG.warn( "addConnection caught Exception: " + ex.getMessage() );
+        }
+        return index;
+    }
+
+
+    /**
+     * *** FORTRESS MOD ****
+     *
+     * Create pool of LDAP connections to server.  Add SSL capability using unboundId's compatibility utility.
+     *
+     * @param size number of connections to generate and store in pool
+     * @throws LDAPException in the event of system error.
+     */
+    private synchronized void setUpPool( int size )
+        throws LDAPException
+    {
+        // Loop on creating connections
+        while ( pool.size() < size )
+        {
+            LDAPConnectionObject co =
+                new LDAPConnectionObject();
+
+            LDAPConnection newConn = createConnection( );
+            newConn.connect( host, port, authdn, authpw );
+            co.setLDAPConn( newConn );
+            co.setInUse( false ); // Mark not in use
+            pool.addElement( co );
+        }
+    }
+
+    /**
+     * Used to manage trust store properties.  If enabled, create SSL connection.
+     *
+     */
+    private static final String ENABLE_LDAP_SSL = "enable.ldap.ssl";
+    private static final String ENABLE_LDAP_SSL_DEBUG = "enable.ldap.ssl.debug";
+    private static final String TRUST_STORE = Config.getProperty( "trust.store" );
+    private static final String TRUST_STORE_PW = Config.getProperty( "trust.store.password" );
+    private static final boolean IS_SSL = (
+        Config.getProperty( ENABLE_LDAP_SSL ) != null   &&
+            Config.getProperty( ENABLE_LDAP_SSL ).equalsIgnoreCase( "true" ) &&
+            TRUST_STORE      != null   &&
+            TRUST_STORE_PW   != null );
+
+    private static final String SET_TRUST_STORE_PROP = "trust.store.set.prop";
+    private static final boolean IS_SET_TRUST_STORE_PROP = (
+        IS_SSL &&
+            Config.getProperty( SET_TRUST_STORE_PROP ) != null   &&
+            Config.getProperty( SET_TRUST_STORE_PROP ).equalsIgnoreCase( "true" ));
+
+    private static final boolean IS_SSL_DEBUG = ( ( Config.getProperty( ENABLE_LDAP_SSL_DEBUG ) != null ) && ( Config
+        .getProperty( ENABLE_LDAP_SSL_DEBUG ).equalsIgnoreCase( "true" ) ) );
+
+    static
+    {
+        if(IS_SET_TRUST_STORE_PROP)
+        {
+            LOG.info( "Set JSSE truststore properties:");
+            LOG.info( "javax.net.ssl.trustStore: " + TRUST_STORE );
+            LOG.info( "javax.net.debug: " + new Boolean( IS_SSL_DEBUG ).toString());
+            System.setProperty( "javax.net.ssl.trustStore", TRUST_STORE );
+            System.setProperty( "javax.net.ssl.trustStorePassword", TRUST_STORE_PW );
+            System.setProperty( "javax.net.debug", new Boolean( IS_SSL_DEBUG ).toString() );
+        }
+    }
+
+    /**
+     * *** FORTRESS MOD ****
+     *
+     * If enabled, use Unbound compatibility lib to create SSL connection.
+     *
+     * @return handle to LDAPConnection
+     * @throws LDAPException wrap GeneralSecurityException or throws ldapexcep.
+     */
+    private LDAPConnection createConnection() throws LDAPException
+    {
+        LDAPConnection newConn = null;
+        if( IS_SSL)
+        {
+            // Generate SSL Connection using Unbound compatibility lib utils:
+            // http://stackoverflow.com/questions/22672477/unboundid-ldap-jdk-migration
+            SSLSocketFactory sslSocketFactory;
+            //SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
+            // These config values set in fortress.properties
+            SSLUtil sslUtil = new SSLUtil(
+                new TrustStoreTrustManager(
+                    TRUST_STORE,
+                    TRUST_STORE_PW.toCharArray() , null, true ) );
+            try
+            {
+                sslSocketFactory = sslUtil.createSSLSocketFactory();
+            }
+            catch(GeneralSecurityException e)
+            {
+                String error = "GeneralSecurityException while creating SSL socket factory=" + e;
+                throw new LDAPException( error, LDAPException.CONNECT_ERROR );
+            }
+            JavaToLDAPSocketFactory ldapSocketFactory =
+                new JavaToLDAPSocketFactory(sslSocketFactory);
+            newConn = new LDAPConnection(ldapSocketFactory);
+        }
+        else
+        {
+            // Make LDAP connection, using template if available
+            newConn = new LDAPConnection();
+        }
+        return newConn;
+    }
+
+    private int find( LDAPConnection con )
+    {
+        // Find the matching Connection in the pool
+        if ( con != null )
+        {
+            for ( int i = 0; i < pool.size(); i++ )
+            {
+                LDAPConnectionObject co =
+                    ( LDAPConnectionObject ) pool.elementAt( i );
+                if ( co.getLDAPConn() == con )
+                {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+
+    /**
+     * Sets the debug printout mode.
+     *
+     * @param mode debug mode to use
+     */
+    public synchronized void setDebug( boolean mode )
+    {
+        debugMode = mode;
+    }
+
+
+    /**
+     * Reports the debug printout mode.
+     *
+     * @return debug mode in use.
+     */
+    public boolean getDebug()
+    {
+        return debugMode;
+    }
+
+
+    private void debug( String s )
+    {
+        if ( debugMode )
+            System.out.println( "ConnectionPool (" +
+                new Date() + ") : " + s );
+    }
+
+
+    private void debug( String s, boolean severe )
+    {
+        if ( debugMode || severe )
+        {
+            System.out.println( "ConnectionPool (" +
+                new Date() + ") : " + s );
+        }
+    }
+
+    /**
+     * Wrapper for LDAPConnection object in pool
+     */
+    class LDAPConnectionObject
+    {
+
+        /**
+         * Returns the associated LDAPConnection.
+         *
+         * @return the LDAPConnection.
+         */
+        LDAPConnection getLDAPConn()
+        {
+            return this.ld;
+        }
+
+
+        /**
+         * Sets the associated LDAPConnection
+         *
+         * @param ld the LDAPConnection
+         */
+        void setLDAPConn( LDAPConnection ld )
+        {
+            this.ld = ld;
+        }
+
+
+        /**
+         * Marks a connection in use or available
+         *
+         * @param inUse <code>true</code> to mark in use, <code>false</code> if available
+         */
+        void setInUse( boolean inUse )
+        {
+            this.inUse = inUse;
+        }
+
+
+        /**
+         * Returns whether the connection is available
+         * for use by another user.
+         *
+         * @return <code>true</code> if available.
+         */
+        boolean isAvailable()
+        {
+            return !inUse;
+        }
+
+
+        /**
+         * Debug method
+         *
+         * @return s user-friendly rendering of the object.
+         */
+        public String toString()
+        {
+            return "LDAPConnection=" + ld + ",inUse=" + inUse;
+        }
+
+        private LDAPConnection ld; // LDAP Connection
+        private boolean inUse; // In use? (true = yes)
+    }
+
+    private final int poolSize; // Min pool size
+    private int poolMax; // Max pool size
+    private final String host; // LDAP host
+    private final int port; // Port to connect at
+    private final String authdn; // Identity of connections
+    private final String authpw; // Password for authdn
+    private LDAPConnection ldc = null; // Connection to clone
+    private java.util.Vector pool; // the actual pool
+    private boolean debugMode;
+}

http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java b/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
new file mode 100644
index 0000000..6819150
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
@@ -0,0 +1,251 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.directory.fortress.core.CfgRuntimeException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implement the X509TrustManager interface which will be used during JSSE truststore manager initialization for LDAP
+ * client-to-server communications over TLS/SSL.
+ * It is used during certificate validation operations within JSSE.
+ * <p/>
+ * Note: This class allows self-signed certificates to pass the validation checks.
+ *
+ * @author Shawn McKinney
+ */
+public final class LdapClientTrustStoreManager implements X509TrustManager, Serializable
+{
+    // Logging
+    private static final String CLS_NM = LdapClientTrustStoreManager.class.getName();
+    private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+    // Config variables
+    private final boolean isExamineValidityDates;
+    private final char[] trustStorePw;
+    private final String trustStoreFile;
+    private final String trustStoreFormat;
+
+    /**
+     * Constructor used by connection configuration utility to load trust store manager.
+     *
+     * @param trustStoreFile    contains fully qualified name of trust store file.
+     * @param trustStorePw      contains the password for trust store
+     * @param trustStoreFormat  contains the format for trust store
+     * @param isExamineValidity boolean var determines if certificate will be examined for valid dates on load.
+     */
+    public LdapClientTrustStoreManager( final String trustStoreFile, final char[] trustStorePw,
+        final String trustStoreFormat, final boolean isExamineValidity )
+    {
+        if ( trustStoreFile == null )
+        {
+            // Cannot continue, throw an unchecked exception:
+            throw new CfgRuntimeException( GlobalErrIds.FT_CONFIG_JSSE_TRUSTSTORE_NULL,
+                "FortressTrustStoreManager constructor : input file name is null" );
+        }
+        // contains the fully-qualified file name of a valid JSSE TrustStore on local file system:
+        this.trustStoreFile = trustStoreFile;
+        // the password to the JSSE TrustStore:
+        this.trustStorePw = trustStorePw;
+        // If true, verify the current date is within the validity period for every certificate in the TrustStore:
+        this.isExamineValidityDates = isExamineValidity;
+        if ( trustStoreFormat == null )
+        {
+            this.trustStoreFormat = KeyStore.getDefaultType();
+        }
+        else
+        {
+            this.trustStoreFormat = trustStoreFormat;
+        }
+    }
+
+    /**
+     * Determine if client certificate is to be trusted.
+     *
+     * @param x509Chain
+     * @param authNType
+     * @throws CertificateException
+     */
+    public synchronized void checkClientTrusted( final X509Certificate[] x509Chain,
+        final String authNType ) throws CertificateException
+    {
+        // For each certificate in the chain, check validity:
+        for ( final X509TrustManager trustMgr : getTrustManagers( x509Chain ) )
+        {
+            trustMgr.checkClientTrusted( x509Chain, authNType );
+        }
+    }
+
+    /**
+     * Determine if server certificate is to be trusted.
+     *
+     * @param x509Chain
+     * @param authNType
+     * @throws CertificateException
+     */
+    public synchronized void checkServerTrusted( final X509Certificate[] x509Chain, final String authNType ) throws
+        CertificateException
+    {
+        for ( final X509TrustManager trustManager : getTrustManagers( x509Chain ) )
+        {
+            trustManager.checkServerTrusted( x509Chain, authNType );
+        }
+    }
+
+    /**
+     * Return the list of accepted issuers for this trust manager.
+     *
+     * @return array of accepted issuers
+     */
+    public synchronized X509Certificate[] getAcceptedIssuers()
+    {
+        return new X509Certificate[0];
+    }
+
+    /**
+     * Return array of trust managers to caller.  Will verify that current date is within certs validity period.
+     *
+     * @param x509Chain contains input X.509 certificate chain.
+     * @return array of X.509 trust managers.
+     * @throws CertificateException if trustStoreFile instance variable is null.
+     */
+    private synchronized X509TrustManager[] getTrustManagers( final X509Certificate[] x509Chain ) throws
+        CertificateException
+    {
+        // If true, verify the current date is within each certificates validity period.
+        if ( isExamineValidityDates )
+        {
+            final Date currentDate = new Date();
+            for ( final X509Certificate x509Cert : x509Chain )
+            {
+                x509Cert.checkValidity( currentDate );
+            }
+        }
+        // The trustStoreFile should contain the fully-qualified name of a Java TrustStore on local file system.
+        final File trustStoreFile = new File( this.trustStoreFile );
+        if ( !trustStoreFile.exists() )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.getTrustManagers : file not found" );
+        }
+        return loadTrustManagers( getTrustStore() );
+    }
+
+    /**
+     * Return an array of X.509 TrustManagers.
+     *
+     * @param trustStore handle to input trustStore
+     * @return array of trust managers
+     * @throws CertificateException if problem occurs during TrustManager initialization.
+     */
+    private X509TrustManager[] loadTrustManagers( final KeyStore trustStore ) throws CertificateException
+    {
+        final X509TrustManager[] x509TrustManagers;
+        try
+        {
+            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory
+                .getDefaultAlgorithm() );
+            trustManagerFactory.init( trustStore );
+            final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+            x509TrustManagers = new X509TrustManager[trustManagers.length];
+            for ( int i = 0; i < trustManagers.length; i++ )
+            {
+                x509TrustManagers[i] = ( X509TrustManager ) trustManagers[i];
+            }
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.loadTrustManagers caught " +
+                "NoSuchAlgorithmException", e );
+        }
+        catch ( KeyStoreException e )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.loadTrustManagers caught KeyStoreException", e );
+        }
+        return x509TrustManagers;
+    }
+
+    /**
+     * Load the TrustStore file into JSSE KeyStore instance.
+     *
+     * @return instance of JSSE KeyStore containing the LDAP Client's TrustStore file info.     *
+     * @throws CertificateException if cannot process file load.
+     */
+    private KeyStore getTrustStore() throws CertificateException
+    {
+        final KeyStore trustStore;
+        try
+        {
+            trustStore = KeyStore.getInstance( trustStoreFormat );
+        }
+        catch ( KeyStoreException e )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught KeyStoreException", e );
+        }
+        FileInputStream trustStoreInputStream = null;
+        try
+        {
+            trustStoreInputStream = new FileInputStream( trustStoreFile );
+            trustStore.load( trustStoreInputStream, trustStorePw );
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught " +
+                "NoSuchAlgorithmException", e );
+        }
+        catch ( IOException e )
+        {
+            throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught KeyStoreException", e );
+        }
+        finally
+        {
+            // Close the input stream.
+            if ( trustStoreInputStream != null )
+            {
+                try
+                {
+                    trustStoreInputStream.close();
+                }
+                catch ( IOException e )
+                {
+                    // Eat this ioexception because it shouldn't be a problem, but log just in case:
+                    LOG.warn( "FortressTrustStoreManager.getTrustManagers finally block on input stream close " +
+                        "operation caught IOException=" + e.getMessage() );
+                }
+            }
+        }
+        return trustStore;
+    }
+}