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

svn commit: r1593724 [1/2] - in /directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client: api/ template/ template/exception/

Author: lucastheisen
Date: Sat May 10 20:04:11 2014
New Revision: 1593724

URL: http://svn.apache.org/r1593724
Log:
DIRAPI-163: Added LdapConnectionTemplate initial implementation

Added:
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRuntimeException.java   (with props)
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/PasswordException.java   (with props)
Modified:
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/DefaultLdapConnectionFactory.java
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionFactory.java
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionPool.java
    directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/PoolableLdapConnectionFactory.java

Modified: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/DefaultLdapConnectionFactory.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/DefaultLdapConnectionFactory.java?rev=1593724&r1=1593723&r2=1593724&view=diff
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/DefaultLdapConnectionFactory.java (original)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/DefaultLdapConnectionFactory.java Sat May 10 20:04:11 2014
@@ -92,6 +92,13 @@ public class DefaultLdapConnectionFactor
 
 
     @Override
+    public LdapApiService getLdapApiService()
+    {
+        return apiService;
+    }
+
+
+    @Override
     public LdapConnection newLdapConnection() throws LdapException
     {
         return bindConnection( newUnboundLdapConnection() );

Modified: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionFactory.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionFactory.java?rev=1593724&r1=1593723&r2=1593724&view=diff
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionFactory.java (original)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionFactory.java Sat May 10 20:04:11 2014
@@ -20,6 +20,7 @@
 package org.apache.directory.ldap.client.api;
 
 
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 
 
@@ -66,6 +67,14 @@ public interface LdapConnectionFactory
 
 
     /**
+     * Returns the LdapApiService instance used by this factory.
+     *
+     * @return The LdapApiService instance used by this factory
+     */
+    public LdapApiService getLdapApiService();
+
+
+    /**
      * Returns a newly created, configured, and authenticated connection. This
      * method should be used by a connection pool to manufacture the pooled
      * instances.

Modified: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionPool.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionPool.java?rev=1593724&r1=1593723&r2=1593724&view=diff
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionPool.java (original)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/LdapConnectionPool.java Sat May 10 20:04:11 2014
@@ -23,6 +23,9 @@ package org.apache.directory.ldap.client
 
 import org.apache.commons.pool.impl.GenericObjectPool;
 import org.apache.directory.api.ldap.codec.api.LdapApiService;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
@@ -35,6 +38,10 @@ import org.apache.directory.api.ldap.cod
  */
 public class LdapConnectionPool extends GenericObjectPool<LdapConnection>
 {
+    private static Logger LOG = LoggerFactory.getLogger( LdapConnectionPool.class );
+
+    private PoolableLdapConnectionFactory factory;
+
 
     /**
      * Instantiates a new LDAP connection pool.
@@ -58,6 +65,18 @@ public class LdapConnectionPool extends 
     public LdapConnectionPool( PoolableLdapConnectionFactory factory )
     {
         super( factory );
+        this.factory = factory;
+    }
+
+
+    /**
+     * Returns the LdapApiService instance used by this connection pool.
+     *
+     * @return The LdapApiService instance used by this connection pool.
+     */
+    public LdapApiService getLdapApiService()
+    {
+        return factory.getLdapApiService();
     }
 
 
@@ -67,9 +86,30 @@ public class LdapConnectionPool extends 
      * @return an LdapConnection object from pool
      * @throws Exception if an error occurs while obtaining a connection from the factory
      */
-    public LdapConnection getConnection() throws Exception
+    public LdapConnection getConnection() throws LdapException
     {
-        return super.borrowObject();
+        LdapConnection connection;
+        try
+        {
+            connection = super.borrowObject();
+        }
+        catch ( LdapException e )
+        {
+            throw ( e );
+        }
+        catch ( RuntimeException e )
+        {
+            throw ( e );
+        }
+        catch ( Exception e )
+        {
+            // wrap in runtime, but this should NEVER happen per published 
+            // contract as it only throws what the makeObject throws and our 
+            // PoolableLdapConnectionFactory only throws LdapException
+            LOG.error( "An unexpected exception was thrown: ", e );
+            throw new RuntimeException( e );
+        }
+        return connection;
     }
 
 
@@ -82,19 +122,19 @@ public class LdapConnectionPool extends 
      * @throws Exception If an error occurs while obtaining a connection 
      * from the factory
      */
-    public LdapConnection getUnboundConnection() throws Exception
+    public LdapConnection getUnboundConnection() throws LdapException
     {
-        LdapConnection connection = super.borrowObject();
+        LdapConnection connection = getConnection();
         connection.unBind();
         return connection;
     }
 
 
-    private static PoolableLdapConnectionFactory newPoolableConnectionFactory( 
-        LdapConnectionConfig connectionConfig, LdapApiService apiService, 
+    private static PoolableLdapConnectionFactory newPoolableConnectionFactory(
+        LdapConnectionConfig connectionConfig, LdapApiService apiService,
         long timeout )
     {
-        DefaultLdapConnectionFactory connectionFactory = 
+        DefaultLdapConnectionFactory connectionFactory =
             new DefaultLdapConnectionFactory( connectionConfig );
         connectionFactory.setLdapApiService( apiService );
         connectionFactory.setTimeOut( timeout );
@@ -108,8 +148,27 @@ public class LdapConnectionPool extends 
      * @param connection the LdapConnection to be released
      * @throws Exception if an error occurs while releasing the connection
      */
-    public void releaseConnection( LdapConnection connection ) throws Exception
+    public void releaseConnection( LdapConnection connection ) throws LdapException
     {
-        super.returnObject( connection );
+        try
+        {
+            super.returnObject( connection );
+        }
+        catch ( LdapException e )
+        {
+            throw ( e );
+        }
+        catch ( RuntimeException e )
+        {
+            throw ( e );
+        }
+        catch ( Exception e )
+        {
+            // wrap in runtime, but this should NEVER happen as it only throws 
+            // what the passivateObject throws and our 
+            // PoolableLdapConnectionFactory only throws LdapException
+            LOG.error( "An unexpected exception was thrown: ", e );
+            throw new RuntimeException( e );
+        }
     }
 }

Modified: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/PoolableLdapConnectionFactory.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/PoolableLdapConnectionFactory.java?rev=1593724&r1=1593723&r2=1593724&view=diff
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/PoolableLdapConnectionFactory.java (original)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/api/PoolableLdapConnectionFactory.java Sat May 10 20:04:11 2014
@@ -24,6 +24,7 @@ package org.apache.directory.ldap.client
 import java.io.IOException;
 
 import org.apache.commons.pool.PoolableObjectFactory;
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 import org.apache.directory.api.ldap.model.name.Dn;
@@ -68,7 +69,7 @@ public class PoolableLdapConnectionFacto
     /**
      * {@inheritDoc}
      */
-    public void activateObject( LdapConnection connection ) throws Exception
+    public void activateObject( LdapConnection connection )
     {
         LOG.debug( "Activating {}", connection );
     }
@@ -77,7 +78,7 @@ public class PoolableLdapConnectionFacto
     /**
      * {@inheritDoc}
      */
-    public void destroyObject( LdapConnection connection ) throws Exception
+    public void destroyObject( LdapConnection connection ) 
     {
         LOG.debug( "Destroying {}", connection );
         try {
@@ -99,9 +100,21 @@ public class PoolableLdapConnectionFacto
 
 
     /**
+     * Returns the LdapApiService instance used by this factory.
+     *
+     * @return The LdapApiService instance used by this factory
+     */
+    public LdapApiService getLdapApiService()
+    {
+        return connectionFactory.getLdapApiService();
+    }
+
+
+    /**
      * {@inheritDoc}
+     * @throws LdapException If unable to connect.
      */
-    public LdapConnection makeObject() throws Exception
+    public LdapConnection makeObject() throws LdapException
     {
         LOG.debug( "Creating a LDAP connection" );
         return connectionFactory.newLdapConnection();
@@ -110,8 +123,9 @@ public class PoolableLdapConnectionFacto
 
     /**
      * {@inheritDoc}
+     * @throws LdapException If unable to reconfigure and rebind.
      */
-    public void passivateObject( LdapConnection connection ) throws Exception
+    public void passivateObject( LdapConnection connection ) throws LdapException
     {
         LOG.debug( "Passivating {}", connection );
         

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java Sat May 10 20:04:11 2014
@@ -0,0 +1,41 @@
+/*
+ *   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.ldap.client.template;
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.ldap.client.api.LdapConnection;
+
+/**
+ * A callback for running code against a managed {@link LdapConnection}.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface ConnectionCallback<T>
+{
+    /**
+     * Provides a managed connection to the implementation of this method.
+     * The implementation is not responsible for open/close or borrow/return.
+     *
+     * @param connection The connection supplied to the implementation.
+     * @return Anything you want
+     * @throws LdapException If you want to
+     */
+    public T doWithConnection( LdapConnection connection ) throws LdapException;
+}
\ No newline at end of file

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ConnectionCallback.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java Sat May 10 20:04:11 2014
@@ -0,0 +1,40 @@
+/*
+ *   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.ldap.client.template;
+
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+
+/**
+ * A callback for processing entries from a search result.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface EntryMapper<T>
+{
+    /**
+     * Will be called once for each entry in the search result.
+     *
+     * @param entry An entry from the search result
+     * @return A object modeling the entry
+     * @throws LdapException If something goes wrong
+     */
+    public T map( Entry entry ) throws LdapException;
+}
\ No newline at end of file

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/EntryMapper.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java Sat May 10 20:04:11 2014
@@ -0,0 +1,241 @@
+/*
+ *   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.ldap.client.template;
+
+
+import java.util.List;
+
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.message.AddRequest;
+import org.apache.directory.api.ldap.model.message.AddResponse;
+import org.apache.directory.api.ldap.model.message.DeleteRequest;
+import org.apache.directory.api.ldap.model.message.DeleteResponse;
+import org.apache.directory.api.ldap.model.message.ModifyRequest;
+import org.apache.directory.api.ldap.model.message.ModifyResponse;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.ResultResponse;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.ldap.client.template.exception.LdapRequestUnsuccessfulException;
+import org.apache.directory.ldap.client.template.exception.PasswordException;
+
+
+/**
+ * Specifies the set of operations available on
+ * {@link org.apache.directory.ldap.client.template.LdapConnectionTemplate
+ * LdapConnectionTemplate}.  This interface can be useful for unit testing
+ * in order to stub out methods.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface LdapConnectionOperations
+{
+
+    /**
+     * Adds an entry specified by an AddRequest to the LDAP server.
+     *
+     * @param addRequest The request
+     * @return An AddResponse
+     */
+    public abstract AddResponse add( AddRequest addRequest );
+
+
+    /**
+     * Adds an entry specified by a Dn and an array of Attribute's to the LDAP
+     * server.
+     *
+     * @param dn The distinguished name of the new entry
+     * @param attributes The attributes of the new entry
+     * @return An AddResponse
+     */
+    public abstract AddResponse add( Dn dn, Attribute... attributes );
+
+
+    /**
+     * Adds an entry specified by a Dn, to be filled out by a RequestBuilder,
+     * to the LDAP server.
+     *
+     * @param dn The distinguished name of the new entry
+     * @param requestBuilder The request builder
+     * @return An AddResponse
+     */
+    public abstract AddResponse add( Dn dn, RequestBuilder<AddRequest> requestBuilder );
+
+
+    /**
+     * Attempts to authenticate the supplied credentials.  If authentication
+     * fails, a PasswordException is thrown.  If successful, the response is 
+     * checked for warnings, and if present, a PasswordWarning is returned.
+     * Otherwise, null is returned.
+     *
+     * @param userDn The distinguished name of the user
+     * @param password The password
+     * @return A PasswordWarning or null
+     * @throws PasswordException If authentication fails
+     */
+    public abstract PasswordWarning authenticate( Dn userDn, char[] password ) throws PasswordException;
+
+
+    /**
+     * Deletes an entry specified by a DeleteRequest from the LDAP server.
+     *
+     * @param deleteRequest The request
+     * @return A DeleteResponse
+     */
+    public abstract DeleteResponse delete( DeleteRequest deleteRequest );
+
+
+    /**
+     * Deletes an entry specified by Dn from the LDAP server.
+     *
+     * @param dn The distinguished name of the entry
+     * @return A DeleteResponse
+     */
+    public abstract DeleteResponse delete( Dn dn );
+
+
+    /**
+     * Deletes an entry specified by Dn, and whose request is configured
+     * by a RequestBuilder, from the LDAP server.
+     *
+     * @param dn The distinguished name of the entry
+     * @param requestBuilder The RequestBuilder
+     * @return A DeleteResponse
+     */
+    public abstract DeleteResponse delete( Dn dn, RequestBuilder<DeleteRequest> requestBuilder );
+
+
+    /**
+     * Executes the <code>connectionCallback</code>, supplying it a managed
+     * connection.
+     *
+     * @param connectionCallback The callback
+     * @return Whatever the callback returns
+     */
+    public abstract <T> T execute( ConnectionCallback<T> connectionCallback );
+
+
+    /**
+     * Performs a lookup, and supplies the matching entry to the 
+     * <code>entryMapper</code>.
+     *
+     * @param dn The distinguished name of the entry
+     * @param entryMapper The mapper from entry to model object
+     * @return Whatever the <code>entryMapper</code> returns
+     */
+    public abstract <T> T lookup( Dn dn, EntryMapper<T> entryMapper );
+
+
+    /**
+     * Performs a lookup, requesting <code>attributes</code>, and supplies 
+     * the matching entry to the <code>entryMapper</code>.
+     *
+     * @param dn The distinguished name of the entry
+     * @param attributes The attributes to be fetched
+     * @param entryMapper The mapper from entry to model object
+     * @return Whatever the <code>entryMapper</code> returns
+     */
+    public abstract <T> T lookup( Dn dn, String[] attributes, EntryMapper<T> entryMapper );
+
+
+    /**
+     * Modifies the password for <code>userDn</code> from 
+     * <code>oldPassword</code> to <code>newPassword</code>, optionally using
+     * an admin account.  If <code>asAdmin</code> is true, then the operation
+     * is performed in admin context which means <code>oldPassword</code> is
+     * may be <code>null</code>.
+     *
+     * @param userDn The distinguished name of the user
+     * @param oldPassword The users old password (optional if asAdmin is true)
+     * @param newPassword The users new password
+     * @param asAdmin If true, execute in admin context
+     * @throws PasswordException If the password modification fails
+     */
+    public abstract void modifyPassword( Dn userDn, char[] oldPassword, char[] newPassword,
+        boolean asAdmin ) throws PasswordException;
+
+
+    /**
+     * Modifies an entry specified by a ModifyRequest on the LDAP server.
+     *
+     * @param modifyRequest The request
+     * @return A ModifyResponse
+     */
+    public abstract ModifyResponse modify( ModifyRequest modifyRequest );
+
+
+    /**
+     * Modifies an entry specified by Dn, and whose request is configured
+     * by a RequestBuilder, on the LDAP server.
+     *
+     * @param dn The distinguished name of the entry
+     * @param requestBuilder The RequestBuilder
+     * @return A ModifyResponse
+     */
+    public abstract ModifyResponse modify( Dn dn, RequestBuilder<ModifyRequest> requestBuilder );
+
+    
+    /**
+     * Checks the supplied response for its result code, and if not 
+     * {@link ResultCodeEnum#SUCCESS}, an exception is thrown. This method is 
+     * intened to be used inline:
+     * 
+     * <pre>
+     * template.responseOrException( template.delete( dn ) );
+     * </pre>
+     *
+     * @param response The response to check for success
+     * @return The supplied <code>response</code>
+     * @throws LdapRequestUnsuccessfulException If the response is not
+     * {@link ResultCodeEnum#SUCCESS}
+     */
+    public <T extends ResultResponse> T responseOrException( T response );
+
+
+    /**
+     * Searches for the first entry matching the supplied 
+     * <code>searchRequest</code>, feeding the result into the 
+     * <code>entryMapper</code>. This is basically the same as 
+     * {@link #search(SearchRequest, EntryMapper)}, but is optimized by
+     * modifying the <code>searchRequest</code> to set its size limit to 1.
+     * The <code>searchRequest</code> is returned to its original size limit
+     * before this method returns (or throws an exception).
+     *
+     * @param searchRequest The search request
+     * @param entryMapper The mapper
+     * @return The mapped entry
+     */
+    public abstract <T> T searchFirst( SearchRequest searchRequest,
+        EntryMapper<T> entryMapper );
+
+
+    /**
+     * Searches for the entries matching the supplied 
+     * <code>searchRequest</code>, feeding the result into the 
+     * <code>entryMapper</code>.
+     *
+     * @param searchRequest The search request
+     * @param entryMapper The mapper
+     * @return The mapped entries
+     */
+    public abstract <T> List<T> search( SearchRequest searchRequest,
+        EntryMapper<T> entryMapper );
+
+}
\ No newline at end of file

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionOperations.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java Sat May 10 20:04:11 2014
@@ -0,0 +1,603 @@
+/*
+ *   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.ldap.client.template;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.directory.api.ldap.extras.controls.ppolicy_impl.PasswordPolicyDecorator;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.Entry;
+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.message.AddRequest;
+import org.apache.directory.api.ldap.model.message.AddResponse;
+import org.apache.directory.api.ldap.model.message.BindRequest;
+import org.apache.directory.api.ldap.model.message.BindRequestImpl;
+import org.apache.directory.api.ldap.model.message.DeleteRequest;
+import org.apache.directory.api.ldap.model.message.DeleteResponse;
+import org.apache.directory.api.ldap.model.message.ModifyRequest;
+import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
+import org.apache.directory.api.ldap.model.message.ModifyResponse;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.ResultResponse;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+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.EntryCursorImpl;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionPool;
+import org.apache.directory.ldap.client.template.exception.LdapRequestUnsuccessfulException;
+import org.apache.directory.ldap.client.template.exception.LdapRuntimeException;
+import org.apache.directory.ldap.client.template.exception.PasswordException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A facade for LDAP operations that handles all of the boiler plate code for 
+ * you allowing more concise operations through the use of callbacks.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * 
+ * @see <a href="http://en.wikipedia.org/wiki/Template_method_pattern">Template method pattern</a>
+ */
+public class LdapConnectionTemplate implements LdapConnectionOperations, ModelFactory
+{
+    private static Logger logger = LoggerFactory.getLogger( LdapConnectionTemplate.class );
+
+    private LdapConnectionPool connectionPool;
+    private final PasswordPolicyDecorator passwordPolicyRequestControl;
+    private PasswordPolicyResponder passwordPolicyResponder;
+    private ModelFactory modelFactory;
+
+
+    /**
+     * Creates a new instance of LdapConnectionTemplate.
+     *
+     * @param connectionPool The pool to obtain connections from.
+     */
+    public LdapConnectionTemplate( LdapConnectionPool connectionPool )
+    {
+        this.connectionPool = connectionPool;
+        this.passwordPolicyRequestControl = new PasswordPolicyDecorator(
+            connectionPool.getLdapApiService() );
+        this.passwordPolicyResponder = new PasswordPolicyResponderImpl(
+            connectionPool.getLdapApiService() );
+        this.modelFactory = new ModelFactoryImpl();
+    }
+
+
+    @Override
+    public AddResponse add( Dn dn, final Attribute... attributes )
+    {
+        return add( dn,
+            new RequestBuilder<AddRequest>()
+            {
+                @Override
+                public void buildRequest( AddRequest request ) throws LdapException
+                {
+                    request.getEntry().add( attributes );
+                }
+            } );
+    }
+
+
+    @Override
+    public AddResponse add( Dn dn, RequestBuilder<AddRequest> requestBuilder )
+    {
+        AddRequest addRequest = newAddRequest( newEntry( dn ) );
+        try
+        {
+            requestBuilder.buildRequest( addRequest );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        return add( addRequest );
+    }
+
+
+    @Override
+    public AddResponse add( AddRequest addRequest )
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+            return connection.add( addRequest );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+    }
+
+
+    @Override
+    public PasswordWarning authenticate( Dn userDn, char[] password ) throws PasswordException
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getUnboundConnection();
+            return authenticateConnection( connection, userDn, password );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            safeCloseLdapConnection( connection );
+        }
+    }
+
+
+    private PasswordWarning authenticateConnection( final LdapConnection connection,
+        final Dn userDn, final char[] password ) throws PasswordException
+    {
+        return passwordPolicyResponder.process(
+            new PasswordPolicyOperation()
+            {
+                @Override
+                public ResultResponse process() throws LdapException
+                {
+                    MemoryClearingBuffer passwordBuffer = MemoryClearingBuffer.newInstance( password );
+                    try
+                    {
+                        BindRequest bindRequest = new BindRequestImpl()
+                            .setDn( userDn )
+                            .setCredentials( passwordBuffer.getBytes() )
+                            .addControl( passwordPolicyRequestControl );
+
+                        return connection.bind( bindRequest );
+                    }
+                    finally
+                    {
+                        passwordBuffer.clear();
+                    }
+                }
+            } );
+    }
+
+
+    @Override
+    public DeleteResponse delete( Dn dn )
+    {
+        return delete( dn, null );
+    }
+
+
+    @Override
+    public DeleteResponse delete( Dn dn, RequestBuilder<DeleteRequest> requestBuilder )
+    {
+        DeleteRequest deleteRequest = newDeleteRequest( dn );
+        if ( requestBuilder != null )
+        {
+            try
+            {
+                requestBuilder.buildRequest( deleteRequest );
+            }
+            catch ( LdapException e )
+            {
+                throw new LdapRuntimeException( e );
+            }
+        }
+        return delete( deleteRequest );
+    }
+
+
+    @Override
+    public DeleteResponse delete( DeleteRequest deleteRequest )
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+            return connection.delete( deleteRequest );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+    }
+
+
+    @Override
+    public <T> T execute( ConnectionCallback<T> connectionCallback )
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+            return connectionCallback.doWithConnection( connection );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+    }
+
+
+    @Override
+    public <T> T lookup( Dn dn, EntryMapper<T> entryMapper )
+    {
+        return lookup( dn, null, entryMapper );
+    }
+
+
+    @Override
+    public <T> T lookup( Dn dn, String[] attributes, EntryMapper<T> entryMapper )
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+            Entry entry = attributes == null
+                ? connection.lookup( dn )
+                : connection.lookup( dn, attributes );
+            return entry == null ? null : entryMapper.map( entry );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+    }
+
+
+    private void modifyPassword( final LdapConnection connection, final Dn userDn,
+        final char[] newPassword ) throws PasswordException
+    {
+        passwordPolicyResponder.process(
+            new PasswordPolicyOperation()
+            {
+                @Override
+                public ResultResponse process() throws PasswordException, LdapException
+                {
+                    // Can't use Password Modify:
+                    // https://issues.apache.org/jira/browse/DIRSERVER-1935
+                    // So revert to regular Modify
+                    MemoryClearingBuffer newPasswordBuffer = MemoryClearingBuffer.newInstance( newPassword );
+                    try
+                    {
+                        ModifyRequest modifyRequest = new ModifyRequestImpl()
+                            .setName( userDn )
+                            .replace( "userPassword", newPasswordBuffer.getComputedBytes() )
+                            .addControl( passwordPolicyRequestControl );
+
+                        return connection.modify( modifyRequest );
+                    }
+                    finally
+                    {
+                        newPasswordBuffer.clear();
+                    }
+                }
+            } );
+
+    }
+
+
+    @Override
+    public void modifyPassword( Dn userDn, char[] oldPassword,
+        char[] newPassword, boolean asAdmin ) throws PasswordException
+    {
+        LdapConnection connection = null;
+        try
+        {
+            if ( asAdmin )
+            {
+                connection = connectionPool.getConnection();
+            }
+            else
+            {
+                connection = connectionPool.getUnboundConnection();
+                authenticateConnection( connection, userDn, oldPassword );
+            }
+
+            modifyPassword( connection, userDn, newPassword );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            if ( asAdmin )
+            {
+                returnLdapConnection( connection );
+            }
+            else
+            {
+                safeCloseLdapConnection( connection );
+            }
+        }
+    }
+
+
+    @Override
+    public ModifyResponse modify( Dn dn, RequestBuilder<ModifyRequest> requestBuilder )
+    {
+        ModifyRequest modifyRequest = newModifyRequest( dn );
+        try
+        {
+            requestBuilder.buildRequest( modifyRequest );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        return modify( modifyRequest );
+    }
+
+
+    @Override
+    public ModifyResponse modify( ModifyRequest modifyRequest )
+    {
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+            return connection.modify( modifyRequest );
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+    }
+
+
+    @Override
+    public AddRequest newAddRequest( Entry entry )
+    {
+        return modelFactory.newAddRequest( entry );
+    }
+
+
+    @Override
+    public Attribute newAttribute( String name, byte[]... values )
+    {
+        return modelFactory.newAttribute( name, values );
+    }
+
+
+    @Override
+    public Attribute newAttribute( String name, String... values )
+    {
+        return modelFactory.newAttribute( name, values );
+    }
+
+
+    @Override
+    public Attribute newAttribute( String name, Value<?>... values )
+    {
+        return modelFactory.newAttribute( name, values );
+    }
+
+
+    @Override
+    public DeleteRequest newDeleteRequest( Dn dn )
+    {
+        return modelFactory.newDeleteRequest( dn );
+    }
+
+
+    @Override
+    public Dn newDn( String dn )
+    {
+        return modelFactory.newDn( dn );
+    }
+
+
+    @Override
+    public Entry newEntry( String dn )
+    {
+        return modelFactory.newEntry( dn );
+    }
+
+
+    @Override
+    public Entry newEntry( Dn dn )
+    {
+        return modelFactory.newEntry( dn );
+    }
+
+
+    @Override
+    public ModifyRequest newModifyRequest( String dn )
+    {
+        return modelFactory.newModifyRequest( dn );
+    }
+
+
+    @Override
+    public ModifyRequest newModifyRequest( Dn dn )
+    {
+        return modelFactory.newModifyRequest( dn );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope )
+    {
+        return modelFactory.newSearchRequest( baseDn, filter, scope );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope )
+    {
+        return modelFactory.newSearchRequest( baseDn, filter, scope );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope, String... attributes )
+    {
+        return modelFactory.newSearchRequest( baseDn, filter, scope, attributes );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope, String... attributes )
+    {
+        return modelFactory.newSearchRequest( baseDn, filter, scope, attributes );
+    }
+
+
+    @Override
+    public <T extends ResultResponse> T responseOrException( T response )
+    {
+        if ( ResultCodeEnum.SUCCESS != response.getLdapResult().getResultCode() )
+        {
+            throw new LdapRequestUnsuccessfulException( response );
+        }
+        return response;
+    }
+
+
+    private void returnLdapConnection( LdapConnection connection )
+    {
+        if ( connection != null )
+        {
+            try
+            {
+                connectionPool.releaseConnection( connection );
+            }
+            catch ( LdapException e )
+            {
+                throw new LdapRuntimeException( e );
+            }
+        }
+    }
+
+
+    private void safeCloseLdapConnection( LdapConnection connection )
+    {
+        if ( connection != null )
+        {
+            try
+            {
+                connection.close();
+            }
+            catch ( IOException e )
+            {
+                logger.error( "Unable to close ldap connection, might be leaking connections:", e.getMessage() );
+                logger.debug( "Unable to close ldap connection, might be leaking connections:", e );
+            }
+        }
+    }
+
+
+    @Override
+    public <T> T searchFirst( SearchRequest searchRequest,
+        EntryMapper<T> entryMapper )
+    {
+        // in case the caller did not set size limit, we cache original value,
+        // set to 1, then set back to original value before returning...
+        long originalSizeLimit = searchRequest.getSizeLimit();
+        try
+        {
+            searchRequest.setSizeLimit( 1 );
+            List<T> entries = search( searchRequest, entryMapper );
+            return entries.isEmpty() ? null : entries.get( 0 );
+        }
+        finally
+        {
+            searchRequest.setSizeLimit( originalSizeLimit );
+        }
+    }
+
+
+    @Override
+    public <T> List<T> search( SearchRequest searchRequest,
+        EntryMapper<T> entryMapper )
+    {
+        List<T> entries = new ArrayList<T>();
+
+        LdapConnection connection = null;
+        try
+        {
+            connection = connectionPool.getConnection();
+
+            for ( Entry entry : new EntryCursorImpl( connection.search( searchRequest ) ) )
+            {
+                entries.add( entryMapper.map( entry ) );
+            }
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        finally
+        {
+            returnLdapConnection( connection );
+        }
+
+        return entries;
+    }
+
+
+    /**
+     * Sets the <code>modelFactory</code> implementation for this facade.
+     *
+     * @param modelFactory The model factory implementation
+     */
+    public void setModelFactory( ModelFactory modelFactory )
+    {
+        this.modelFactory = modelFactory;
+    }
+
+
+    /**
+     * Sets the <code>passwordPolicyResponder</code> implementation for this
+     * facade.
+     *
+     * @param passwordPolicyResponder The password policy responder 
+     * implementation
+     */
+    public void setPasswordPolicyResponder( PasswordPolicyResponder passwordPolicyResponder )
+    {
+        this.passwordPolicyResponder = passwordPolicyResponder;
+    }
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/LdapConnectionTemplate.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java Sat May 10 20:04:11 2014
@@ -0,0 +1,245 @@
+/*
+ *   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.ldap.client.template;
+
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+
+/**
+ * A buffer for storing sensitive information like passwords.  It provides 
+ * useful operations for characters such as character encoding/decoding, 
+ * whitespace trimming, and lowercasing.  It can be cleared out when operations
+ * are complete.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class MemoryClearingBuffer
+{
+    private static final Charset UTF8 = Charset.forName( "UTF-8" );
+    private byte[] computedBytes;
+    private char[] computedChars;
+    private byte[] originalBytes;
+    private char[] originalChars;
+    private char[] precomputedChars;
+
+
+    private MemoryClearingBuffer( byte[] originalBytes, char[] originalChars, boolean trim, boolean lowerCase )
+    {
+        this.originalBytes = originalBytes;
+        this.originalChars = originalChars;
+
+        if ( trim || lowerCase )
+        {
+            if ( this.originalChars == null )
+            {
+                throw new UnsupportedOperationException( "trim and lowerCase only applicable to char[]" );
+            }
+
+            char[] working = Arrays.copyOf( originalChars, originalChars.length );
+            int startIndex = 0;
+            int endIndex = working.length;
+
+            if ( trim )
+            {
+                // ltrim
+                for ( ; startIndex < working.length; startIndex++ )
+                {
+                    if ( !Character.isWhitespace( working[startIndex] ) )
+                        break;
+                }
+
+                // rtrim
+                for ( endIndex--; endIndex > startIndex; endIndex-- )
+                {
+                    if ( !Character.isWhitespace( working[endIndex] ) )
+                        break;
+                }
+                endIndex++;
+            }
+
+            if ( lowerCase )
+            {
+                // lower case
+                for ( int i = startIndex; i < endIndex; i++ )
+                {
+                    working[i] = Character.toLowerCase( working[i] );
+                }
+            }
+
+            this.precomputedChars = new char[endIndex - startIndex];
+            System.arraycopy( working, startIndex, this.precomputedChars, 0, endIndex - startIndex );
+        }
+        else
+        {
+            this.precomputedChars = this.originalChars;
+        }
+    }
+
+
+    /**
+     * Creates a new instance of MemoryClearingBuffer from a 
+     * <code>byte[]</code>.
+     *
+     * @param bytes A byte[]
+     * @return A buffer
+     */
+    public static MemoryClearingBuffer newInstance( byte[] bytes )
+    {
+        return new MemoryClearingBuffer( bytes, null, false, false );
+    }
+
+
+    /**
+     * Creates a new instance of MemoryClearingBuffer from a 
+     * <code>char[]</code>.
+     *
+     * @param chars A char[]
+     * @return A buffer
+     */
+    public static MemoryClearingBuffer newInstance( char[] chars )
+    {
+        return new MemoryClearingBuffer( null, chars, false, false );
+    }
+
+
+    /**
+     * Creates a new instance of MemoryClearingBuffer from a 
+     * <code>char[]</code>, optionally performing whitespace trimming and
+     * conversion to lower case.
+     *
+     * @param chars A char[]
+     * @param trim If true, whitespace will be trimmed off of both ends of the
+     * <code>char[]</code>
+     * @param lowerCase If true, the characters will be converted to lower case
+     * @return A buffer
+     */
+    public static MemoryClearingBuffer newInstance( char[] chars, boolean trim, boolean lowerCase )
+    {
+        return new MemoryClearingBuffer( null, chars, trim, lowerCase );
+    }
+
+
+    /**
+     *  Clears the buffer out, filling its cells with null.
+     */
+    public void clear()
+    {
+        // clear out computed memory
+        if ( computedBytes != null )
+        {
+            Arrays.fill( computedBytes, ( byte ) 0 );
+        }
+        if ( computedChars != null )
+        {
+            Arrays.fill( computedChars, '0' );
+        }
+        if ( precomputedChars != null )
+        {
+            Arrays.fill( precomputedChars, '0' );
+        }
+
+        computedBytes = null;
+        computedChars = null;
+        originalBytes = null;
+        originalChars = null;
+        precomputedChars = null;
+    }
+
+
+    /**
+     * Returns a UTF8 encoded <code>byte[]</code> representation of the 
+     * <code>char[]</code> used to create this buffer.
+     * 
+     * @return A byte[]
+     */
+    byte[] getComputedBytes()
+    {
+        if ( computedBytes == null )
+        {
+            ByteBuffer byteBuffer = UTF8.encode(
+                CharBuffer.wrap( precomputedChars, 0, precomputedChars.length ) );
+            computedBytes = new byte[byteBuffer.remaining()];
+            byteBuffer.get( computedBytes );
+
+            // clear out the temporary bytebuffer
+            byteBuffer.flip();
+            byte[] nullifier = new byte[byteBuffer.limit()];
+            Arrays.fill( nullifier, ( byte ) 0 );
+            byteBuffer.put( nullifier );
+        }
+        return computedBytes;
+    }
+
+
+    /**
+     * Returns a UTF8 decoded <code>char[]</code> representation of the 
+     * <code>byte[]</code> used to create this buffer.
+     *
+     * @return A char[]
+     */
+    private char[] getComputedChars()
+    {
+        if ( computedChars == null )
+        {
+            CharBuffer charBuffer = UTF8.decode(
+                ByteBuffer.wrap( originalBytes, 0, originalBytes.length ) );
+            computedChars = new char[charBuffer.remaining()];
+            charBuffer.get( computedChars );
+
+            // clear out the temporary bytebuffer
+            charBuffer.flip();
+            char[] nullifier = new char[charBuffer.limit()];
+            Arrays.fill( nullifier, ( char ) 0 );
+            charBuffer.put( nullifier );
+        }
+        return computedChars;
+    }
+
+
+    /**
+     * Returns the <code>byte[]</code> used to create this buffer, or 
+     * {@link #getComputedBytes()} if created with a <code>char[]</code>.
+     *
+     * @return A byte[]
+     */
+    public byte[] getBytes()
+    {
+        return originalBytes == null
+            ? getComputedBytes()
+            : originalBytes;
+    }
+
+    /**
+     * Returns the <code>char[]</code> used to create this buffer, or 
+     * {@link #getComputedChars()} if created with a <code>byte[]</code>.
+     *
+     * @return A byte[]
+     */
+    public char[] getChars()
+    {
+        return precomputedChars == null
+            ? getComputedChars()
+            : precomputedChars;
+    }
+}
\ No newline at end of file

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/MemoryClearingBuffer.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java Sat May 10 20:04:11 2014
@@ -0,0 +1,85 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.message.AddRequest;
+import org.apache.directory.api.ldap.model.message.DeleteRequest;
+import org.apache.directory.api.ldap.model.message.ModifyRequest;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.api.ldap.model.name.Dn;
+
+
+/**
+ * A factory for creating {@link org.apache.directory.api.ldap.model} objects.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface ModelFactory
+{
+    public AddRequest newAddRequest( Entry entry );
+
+
+    public Attribute newAttribute( String name, byte[]... values );
+    
+    
+    public Attribute newAttribute( String name, String... values );
+    
+    
+    public Attribute newAttribute( String name, Value<?>... values );
+
+
+    public DeleteRequest newDeleteRequest( Dn dn );
+
+
+    public Dn newDn( String dn );
+
+
+    public Entry newEntry( String dn );
+
+
+    public Entry newEntry( Dn dn );
+
+
+    public ModifyRequest newModifyRequest( String dn );
+
+
+    public ModifyRequest newModifyRequest( Dn dn );
+
+
+    public SearchRequest newSearchRequest( String baseDn, String filter,
+        SearchScope scope );
+
+
+    public SearchRequest newSearchRequest( Dn baseDn, String filter,
+        SearchScope scope );
+
+
+    public SearchRequest newSearchRequest( String baseDn, String filter,
+        SearchScope scope, String... attributes );
+
+
+    public SearchRequest newSearchRequest( Dn baseDn, String filter,
+        SearchScope scope, String... attributes );
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactory.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java Sat May 10 20:04:11 2014
@@ -0,0 +1,173 @@
+/*
+ *   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.ldap.client.template;
+
+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.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.Entry;
+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.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.message.AddRequest;
+import org.apache.directory.api.ldap.model.message.AddRequestImpl;
+import org.apache.directory.api.ldap.model.message.DeleteRequest;
+import org.apache.directory.api.ldap.model.message.DeleteRequestImpl;
+import org.apache.directory.api.ldap.model.message.ModifyRequest;
+import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
+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.template.exception.LdapRuntimeException;
+
+
+/**
+ * The default implementation of {@link ModelFactory}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class ModelFactoryImpl implements ModelFactory
+{
+    @Override
+    public AddRequest newAddRequest( Entry entry )
+    {
+        return new AddRequestImpl().setEntry( entry );
+    }
+
+
+    @Override
+    public Attribute newAttribute( String name, byte[]... values )
+    {
+        return new DefaultAttribute( name, values );
+    }
+
+    
+    @Override
+    public Attribute newAttribute( String name, String... values )
+    {
+        return new DefaultAttribute( name, values );
+    }
+    
+
+    @Override
+    public Attribute newAttribute( String name, Value<?>... values )
+    {
+        return new DefaultAttribute( name, values );
+    }
+
+
+    @Override
+    public DeleteRequest newDeleteRequest( Dn dn )
+    {
+        return new DeleteRequestImpl()
+            .setName( dn );
+    }
+
+
+    @Override
+    public Dn newDn( String dn )
+    {
+        try
+        {
+            return new Dn( dn );
+        }
+        catch ( LdapInvalidDnException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+    }
+
+
+    @Override
+    public Entry newEntry( String dn )
+    {
+        return newEntry( newDn( dn ) );
+    }
+
+
+    @Override
+    public Entry newEntry( Dn dn )
+    {
+        return new DefaultEntry( dn );
+    }
+
+
+    @Override
+    public ModifyRequest newModifyRequest( String dn )
+    {
+        return newModifyRequest( newDn( dn ) );
+    }
+
+
+    @Override
+    public ModifyRequest newModifyRequest( Dn dn )
+    {
+        return new ModifyRequestImpl().setName( dn );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( String baseDn, String filter,
+        SearchScope scope )
+    {
+        return newSearchRequest( newDn( baseDn ), filter, scope );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( Dn baseDn, String filter,
+        SearchScope scope )
+    {
+        return newSearchRequest( baseDn, filter, scope, ( String[] ) null );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( String baseDn, String filter,
+        SearchScope scope, String... attributes )
+    {
+        return newSearchRequest( newDn( baseDn ), filter, scope, attributes );
+    }
+
+
+    @Override
+    public SearchRequest newSearchRequest( Dn baseDn, String filter,
+        SearchScope scope, String... attributes )
+    {
+        SearchRequest searchRequest = null;
+        try
+        {
+            searchRequest = new SearchRequestImpl()
+                .setBase( baseDn )
+                .setFilter( filter )
+                .setScope( scope == null ? SearchScope.OBJECT : scope );
+            if ( attributes != null && attributes.length > 0 )
+            {
+                searchRequest.addAttributes( attributes );
+            }
+        }
+        catch ( LdapException e )
+        {
+            throw new LdapRuntimeException( e );
+        }
+        return searchRequest;
+    }
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/ModelFactoryImpl.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java Sat May 10 20:04:11 2014
@@ -0,0 +1,46 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.message.ResultResponse;
+import org.apache.directory.ldap.client.template.exception.PasswordException;
+
+
+/**
+ * An callback for processing requests whose success/failure imply some sort
+ * of password policy information.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface PasswordPolicyOperation
+{
+    /**
+     * Execute operations whose results imply somme sort of password policy
+     * information.
+     *
+     * @return The final response of the operation
+     * @throws PasswordException If there was a failure for a password policy
+     * reason
+     * @throws LdapException If there was an general ldap failure
+     */
+    public ResultResponse process() throws PasswordException, LdapException;
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyOperation.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java Sat May 10 20:04:11 2014
@@ -0,0 +1,49 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.ldap.client.template.exception.PasswordException;
+
+
+/**
+ * A class for translating the outcome of a {@link PasswordPolicyOperation}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface PasswordPolicyResponder
+{
+    /**
+     * Execute the <code>operation</code> and translate the outcome as follows:
+     * 
+     * <ul>
+     * <li>SUCCESS: return null</li>
+     * <li>WARNING: return {@link PasswordWarning}</li>
+     * <li>FAILURE: throw {@link PasswordException}</li>
+     * </ul>
+     * 
+     * @param operation An operation whose outcome implies password policy 
+     * information
+     * @return A <code>PasswordWarning</code> if warnings are present, or null 
+     * if completely successful.
+     * @throws PasswordException If the <code>operation</code> was a failure.
+     */
+    public PasswordWarning process( PasswordPolicyOperation operation ) throws PasswordException;
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponder.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java Sat May 10 20:04:11 2014
@@ -0,0 +1,96 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
+import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicy;
+import org.apache.directory.api.ldap.extras.controls.ppolicy_impl.PasswordPolicyDecorator;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.message.Control;
+import org.apache.directory.api.ldap.model.message.Response;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.ResultResponse;
+import org.apache.directory.ldap.client.template.exception.PasswordException;
+
+
+/**
+ * The default implementation of {@link PasswordPolicyResponder}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class PasswordPolicyResponderImpl implements PasswordPolicyResponder
+{
+    private final PasswordPolicyDecorator passwordPolicyRequestControl;
+
+
+    public PasswordPolicyResponderImpl( LdapApiService ldapApiService )
+    {
+        this.passwordPolicyRequestControl = new PasswordPolicyDecorator(
+            ldapApiService );
+    }
+
+
+    private PasswordPolicy getPasswordPolicy( Response response )
+    {
+        Control control = response.getControls().get( passwordPolicyRequestControl.getOid() );
+        return control == null
+            ? null
+            : ( ( PasswordPolicyDecorator ) control ).getDecorated();
+    }
+
+
+    @Override
+    public PasswordWarning process( PasswordPolicyOperation operation )
+        throws PasswordException
+    {
+        try
+        {
+            ResultResponse response = operation.process();
+            PasswordPolicy passwordPolicy = getPasswordPolicy( response );
+
+            ResultCodeEnum resultCode = response.getLdapResult().getResultCode();
+            if ( resultCode == ResultCodeEnum.SUCCESS )
+            {
+                if ( passwordPolicy != null )
+                {
+                    return PasswordWarningImpl.newWarning( passwordPolicy );
+                }
+                return null;
+            }
+            else
+            {
+                PasswordException exception = new PasswordException();
+                exception.setResultCode( resultCode );
+                if ( passwordPolicy != null
+                    && passwordPolicy.getResponse() != null
+                    && passwordPolicy.getResponse().getPasswordPolicyError() != null )
+                {
+                    exception.setPasswordPolicyError( passwordPolicy.getResponse().getPasswordPolicyError() );
+                }
+                throw exception;
+            }
+        }
+        catch ( LdapException e )
+        {
+            throw new PasswordException().setLdapException( e );
+        }
+    }
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordPolicyResponderImpl.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java Sat May 10 20:04:11 2014
@@ -0,0 +1,56 @@
+/*
+ *   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.ldap.client.template;
+
+
+import java.io.Serializable;
+
+
+/**
+ * A container for password warning information.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface PasswordWarning extends Serializable
+{
+    /**
+     * Returns the number of seconds before the password will expire.
+     *
+     * @return The number of seconds before the password will expire
+     */
+    public int getTimeBeforeExpiration();
+
+
+    /**
+     * Returns the number of remaining authentications before the account will 
+     * be locked.
+     *
+     * @return The number of authentications before lockout
+     */
+    public int getGraceAuthNsRemaining();
+
+
+    /**
+     * Returns true, if a password reset is required.
+     *
+     * @return True, if a password reset is required
+     */
+    public boolean isChangeAfterReset();
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarning.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java Sat May 10 20:04:11 2014
@@ -0,0 +1,89 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicy;
+import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyErrorEnum;
+import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyResponse;
+
+
+/**
+ * The default implementation of {@link PasswordWarning}.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+class PasswordWarningImpl implements PasswordWarning
+{
+    private static final long serialVersionUID = -8952246313604352357L;
+
+    private int timeBeforeExpiration = -1;
+    private int graceAuthNsRemaining = -1;
+    private boolean changeAfterReset = false;
+
+
+    private PasswordWarningImpl()
+    {
+    }
+
+
+    public static PasswordWarning newWarning( PasswordPolicy policy )
+    {
+        PasswordPolicyResponse response = policy.getResponse();
+        if ( response != null )
+        {
+            PasswordWarningImpl policyWarning = new PasswordWarningImpl();
+            policyWarning.timeBeforeExpiration = response.getTimeBeforeExpiration();
+            policyWarning.graceAuthNsRemaining = response.getGraceAuthNRemaining();
+            policyWarning.changeAfterReset = response.getPasswordPolicyError() ==
+                PasswordPolicyErrorEnum.CHANGE_AFTER_RESET;
+
+            if ( policyWarning.timeBeforeExpiration >= 0 || policyWarning.graceAuthNsRemaining >= 0
+                || policyWarning.changeAfterReset )
+            {
+                // it actually is a warning!
+                return policyWarning;
+            }
+        }
+        return null;
+    }
+
+
+    @Override
+    public int getTimeBeforeExpiration()
+    {
+        return timeBeforeExpiration;
+    }
+
+
+    @Override
+    public int getGraceAuthNsRemaining()
+    {
+        return graceAuthNsRemaining;
+    }
+
+
+    @Override
+    public boolean isChangeAfterReset()
+    {
+        return changeAfterReset;
+    }
+}
\ No newline at end of file

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/PasswordWarningImpl.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java Sat May 10 20:04:11 2014
@@ -0,0 +1,44 @@
+/*
+ *   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.ldap.client.template;
+
+
+import org.apache.directory.api.ldap.model.exception.LdapException;
+
+
+/**
+ * Edits a supplied request adding specifics.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface RequestBuilder<T>
+{
+    /**
+     * Modifies the provided request adding specific information to it.
+     * The supplied request is typically a factory built request with just
+     * its Dn set.  It is the responsibility of this method to fill in all
+     * of the details.  The request will be sent to the LDAP server upon
+     * upon returning from this method.
+     *
+     * @param request The request to be modified
+     * @throws LdapException If something goes wrong
+     */
+    public void buildRequest( T request ) throws LdapException;
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/RequestBuilder.java
------------------------------------------------------------------------------
    svn:executable = *

Added: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java?rev=1593724&view=auto
==============================================================================
--- directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java (added)
+++ directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java Sat May 10 20:04:11 2014
@@ -0,0 +1,51 @@
+/*
+ *  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.ldap.client.template.exception;
+
+
+import org.apache.directory.api.ldap.model.message.ResultResponse;
+
+
+/**
+ * An RuntimeException wrapper class that allows the user to choose to have
+ * unsuccessful responses thrown as exceptions rather than checking the 
+ * response itself for process flow.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class LdapRequestUnsuccessfulException extends RuntimeException
+{
+    private static final long serialVersionUID = 1982294624076306127L;
+
+    private ResultResponse response;
+
+
+    public LdapRequestUnsuccessfulException( ResultResponse response )
+    {
+        super();
+    }
+
+
+    public ResultResponse getResponse()
+    {
+        return response;
+    }
+
+}

Propchange: directory/shared/trunk/ldap/client/api/src/main/java/org/apache/directory/ldap/client/template/exception/LdapRequestUnsuccessfulException.java
------------------------------------------------------------------------------
    svn:executable = *