This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 393c65a6e5196b8b167e679784957c290c7c976f Author: Benoit Tellier <[email protected]> AuthorDate: Sun Jun 6 13:13:53 2021 +0700 JAMES-3594 Implement ReadOnlyLDAPUsersDAO with UnboundID library --- pom.xml | 6 +- server/data/data-ldap/pom.xml | 4 + .../user/ldap/LdapRepositoryConfiguration.java | 22 +- .../apache/james/user/ldap/ReadOnlyLDAPUser.java | 76 ++-- .../james/user/ldap/ReadOnlyLDAPUsersDAO.java | 266 ++++------- .../apache/james/user/ldap/api/LdapConstants.java | 32 -- .../user/ldap/retry/DoublingRetrySchedule.java | 99 ---- .../user/ldap/retry/ExceptionRetryHandler.java | 131 ------ .../ldap/retry/api/ExceptionRetryingProxy.java | 49 -- .../james/user/ldap/retry/api/RetryHandler.java | 52 --- .../james/user/ldap/retry/api/RetrySchedule.java | 35 -- .../ldap/retry/naming/LoggingRetryHandler.java | 54 --- .../retry/naming/NamingExceptionRetryHandler.java | 72 --- .../user/ldap/retry/naming/RetryingContext.java | 497 --------------------- .../retry/naming/directory/RetryingDirContext.java | 407 ----------------- .../retry/naming/ldap/RetryingLdapContext.java | 129 ------ .../user/ldap/retry/DoublingRetryScheduleTest.java | 71 --- .../user/ldap/retry/ExceptionRetryHandlerTest.java | 153 ------- .../naming/NamingExceptionRetryHandlerTest.java | 92 ---- 19 files changed, 132 insertions(+), 2115 deletions(-) diff --git a/pom.xml b/pom.xml index f7bc55a..21e035a 100644 --- a/pom.xml +++ b/pom.xml @@ -2105,7 +2105,6 @@ <artifactId>metrics-elasticsearch-reporter</artifactId> <version>${es-reporter.version}</version> </dependency> - <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> @@ -2153,6 +2152,11 @@ <version>2.9.2</version> </dependency> <dependency> + <groupId>com.unboundid</groupId> + <artifactId>unboundid-ldapsdk</artifactId> + <version>6.0.0</version> + </dependency> + <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> diff --git a/server/data/data-ldap/pom.xml b/server/data/data-ldap/pom.xml index 88492e1..aec06a1 100644 --- a/server/data/data-ldap/pom.xml +++ b/server/data/data-ldap/pom.xml @@ -70,6 +70,10 @@ <artifactId>guavate</artifactId> </dependency> <dependency> + <groupId>com.unboundid</groupId> + <artifactId>unboundid-ldapsdk</artifactId> + </dependency> + <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </dependency> diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/LdapRepositoryConfiguration.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/LdapRepositoryConfiguration.java index f526cd2..efc79ed 100644 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/LdapRepositoryConfiguration.java +++ b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/LdapRepositoryConfiguration.java @@ -32,9 +32,8 @@ import com.google.common.base.Preconditions; public class LdapRepositoryConfiguration { public static final String SUPPORTS_VIRTUAL_HOSTING = "supportsVirtualHosting"; - private static final boolean NO_CONNECTION_POOL = false; - private static final int NO_CONNECTION_TIMEOUT = -1; - private static final int NO_READ_TIME_OUT = -1; + private static final int NO_CONNECTION_TIMEOUT = 0; + private static final int NO_READ_TIME_OUT = 0; private static final boolean ENABLE_VIRTUAL_HOSTING = true; private static final ReadOnlyLDAPGroupRestriction NO_RESTRICTION = new ReadOnlyLDAPGroupRestriction(null); private static final String NO_FILTER = null; @@ -134,7 +133,6 @@ public class LdapRepositoryConfiguration { userBase.get(), userIdAttribute.get(), userObjectClass.get(), - NO_CONNECTION_POOL, NO_CONNECTION_TIMEOUT, NO_READ_TIME_OUT, maxRetries.get(), @@ -160,7 +158,6 @@ public class LdapRepositoryConfiguration { String userIdAttribute = configuration.getString("[@userIdAttribute]"); String userObjectClass = configuration.getString("[@userObjectClass]"); // Default is to use connection pooling - boolean useConnectionPool = configuration.getBoolean("[@useConnectionPool]", NO_CONNECTION_POOL); int connectionTimeout = configuration.getInt("[@connectionTimeout]", NO_CONNECTION_TIMEOUT); int readTimeout = configuration.getInt("[@readTimeout]", NO_READ_TIME_OUT); // Default maximum retries is 1, which allows an alternate connection to @@ -193,7 +190,6 @@ public class LdapRepositoryConfiguration { userBase, userIdAttribute, userObjectClass, - useConnectionPool, connectionTimeout, readTimeout, maxRetries, @@ -251,9 +247,6 @@ public class LdapRepositoryConfiguration { */ private final String userObjectClass; - // Use a connection pool. Default is true. - private final boolean useConnectionPool; - // The connection timeout in milliseconds. // A value of less than or equal to zero means to use the network protocol's // (i.e., TCP's) timeout value. @@ -290,7 +283,7 @@ public class LdapRepositoryConfiguration { private final Optional<Username> administratorId; private LdapRepositoryConfiguration(String ldapHost, String principal, String credentials, String userBase, String userIdAttribute, - String userObjectClass, boolean useConnectionPool, int connectionTimeout, int readTimeout, + String userObjectClass, int connectionTimeout, int readTimeout, int maxRetries, boolean supportsVirtualHosting, long retryStartInterval, long retryMaxInterval, int scale, ReadOnlyLDAPGroupRestriction restriction, String filter, Optional<String> administratorId) throws ConfigurationException { @@ -300,7 +293,6 @@ public class LdapRepositoryConfiguration { this.userBase = userBase; this.userIdAttribute = userIdAttribute; this.userObjectClass = userObjectClass; - this.useConnectionPool = useConnectionPool; this.connectionTimeout = connectionTimeout; this.readTimeout = readTimeout; this.maxRetries = maxRetries; @@ -351,9 +343,6 @@ public class LdapRepositoryConfiguration { return userObjectClass; } - public boolean useConnectionPool() { - return useConnectionPool; - } public int getConnectionTimeout() { return connectionTimeout; @@ -400,8 +389,7 @@ public class LdapRepositoryConfiguration { if (o instanceof LdapRepositoryConfiguration) { LdapRepositoryConfiguration that = (LdapRepositoryConfiguration) o; - return Objects.equals(this.useConnectionPool, that.useConnectionPool) - && Objects.equals(this.connectionTimeout, that.connectionTimeout) + return Objects.equals(this.connectionTimeout, that.connectionTimeout) && Objects.equals(this.readTimeout, that.readTimeout) && Objects.equals(this.maxRetries, that.maxRetries) && Objects.equals(this.supportsVirtualHosting, that.supportsVirtualHosting) @@ -423,7 +411,7 @@ public class LdapRepositoryConfiguration { @Override public final int hashCode() { - return Objects.hash(ldapHost, principal, credentials, userBase, userIdAttribute, userObjectClass, useConnectionPool, + return Objects.hash(ldapHost, principal, credentials, userBase, userIdAttribute, userObjectClass, connectionTimeout, readTimeout, maxRetries, supportsVirtualHosting, retryStartInterval, retryMaxInterval, scale, restriction, filter, administratorId); } diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java index 6946fa6..6125fb9 100644 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java +++ b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java @@ -21,13 +21,14 @@ package org.apache.james.user.ldap; import java.io.Serializable; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.ldap.LdapContext; - import org.apache.james.core.Username; import org.apache.james.user.api.model.User; -import org.apache.james.user.ldap.api.LdapConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.unboundid.ldap.sdk.BindResult; +import com.unboundid.ldap.sdk.LDAPConnectionPool; +import com.unboundid.ldap.sdk.LDAPException; /** * Encapsulates the details of a user as taken from an LDAP compliant directory. @@ -38,14 +39,13 @@ import org.apache.james.user.ldap.api.LdapConstants; * means of authenticating the user against the LDAP server. Consequently * invocations of the contract method {@link User#setPassword(String)} always * returns <code>false</code>. - * - * @see SimpleLDAPConnection + * * @see ReadOnlyUsersLDAPRepository * */ public class ReadOnlyLDAPUser implements User, Serializable { - // private static final long serialVersionUID = -6712066073820393235L; - private static final long serialVersionUID = -5201235065842464013L; + private static final long serialVersionUID = -5201235065842464014L; + public static final Logger LOGGER = LoggerFactory.getLogger(ReadOnlyLDAPUser.class); /** * The user's identifier or name. This is the value that is returned by the @@ -55,31 +55,28 @@ public class ReadOnlyLDAPUser implements User, Serializable { * <code>"myorg.com"</code>, the user's email address will be * <code>"john.bold@myorg.com"</code>. */ - private Username userName; + private final Username userName; /** * The distinguished name of the user-record in the LDAP directory. */ - private String userDN; + private final String userDN; /** * The context for the LDAP server from which to retrieve the * user's details. */ - private LdapContext ldapContext = null; - - /** - * Creates a new instance of ReadOnlyLDAPUser. - * - */ - private ReadOnlyLDAPUser() { - super(); - } + private final LDAPConnectionPool connectionPool; /** * Constructs an instance for the given user-details, and which will * authenticate against the given host. - * + * + * @param connectionPool + * The connectionPool for the LDAP server on which the user details are held. + * This is also the host against which the user will be + * authenticated, when {@link #verifyPassword(String)} is + * invoked. * @param userName * The user-identifier/name. This is the value with which the * field will be initialised, and which will be @@ -87,18 +84,11 @@ public class ReadOnlyLDAPUser implements User, Serializable { * @param userDN * The distinguished (unique-key) of the user details as stored * on the LDAP directory. - * @param ldapContext - * The context for the LDAP server on which the user details are held. - * This is also the host against which the user will be - * authenticated, when {@link #verifyPassword(String)} is - * invoked. - * @throws NamingException */ - public ReadOnlyLDAPUser(Username userName, String userDN, LdapContext ldapContext) { - this(); + public ReadOnlyLDAPUser(Username userName, String userDN, LDAPConnectionPool connectionPool) { this.userName = userName; this.userDN = userDN; - this.ldapContext = ldapContext; + this.connectionPool = connectionPool; } /** @@ -139,27 +129,13 @@ public class ReadOnlyLDAPUser implements User, Serializable { */ @Override public boolean verifyPassword(String password) { - boolean result = false; - LdapContext ldapContext = null; try { - ldapContext = this.ldapContext.newInstance(null); - ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, - LdapConstants.SECURITY_AUTHENTICATION_SIMPLE); - ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN); - ldapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, password); - ldapContext.reconnect(null); - result = true; - } catch (NamingException exception) { - // no-op - } finally { - if (null != ldapContext) { - try { - ldapContext.close(); - } catch (NamingException ex) { - // no-op - } - } + BindResult bindResult = connectionPool.bindAndRevertAuthentication(userDN, password); + return bindResult.getResultCode() + .intValue() == 0; + } catch (LDAPException e) { + LOGGER.error("Unexpected error upon authentication", e); + return false; } - return result; } } diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUsersDAO.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUsersDAO.java index 678a96a..fb450d0 100644 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUsersDAO.java +++ b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUsersDAO.java @@ -19,26 +19,18 @@ package org.apache.james.user.ldap; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Properties; import java.util.Set; +import javax.annotation.PreDestroy; import javax.inject.Inject; -import javax.naming.Context; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.naming.ldap.InitialLdapContext; -import javax.naming.ldap.LdapContext; +import javax.net.SocketFactory; import org.apache.commons.configuration2.HierarchicalConfiguration; import org.apache.commons.configuration2.ex.ConfigurationException; @@ -49,39 +41,26 @@ import org.apache.james.core.Username; import org.apache.james.lifecycle.api.Configurable; import org.apache.james.user.api.UsersRepositoryException; import org.apache.james.user.api.model.User; -import org.apache.james.user.ldap.api.LdapConstants; -import org.apache.james.user.ldap.retry.DoublingRetrySchedule; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.apache.james.user.ldap.retry.naming.ldap.RetryingLdapContext; import org.apache.james.user.lib.UsersDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.fge.lambdas.Throwing; import com.github.steveash.guavate.Guavate; -import com.google.common.base.Strings; +import com.unboundid.ldap.sdk.LDAPConnection; +import com.unboundid.ldap.sdk.LDAPConnectionOptions; +import com.unboundid.ldap.sdk.LDAPConnectionPool; +import com.unboundid.ldap.sdk.LDAPException; +import com.unboundid.ldap.sdk.SearchResult; +import com.unboundid.ldap.sdk.SearchResultEntry; +import com.unboundid.ldap.sdk.SearchScope; public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { private static final Logger LOGGER = LoggerFactory.getLogger(ReadOnlyLDAPUsersDAO.class); - // The name of the factory class which creates the initial context - // for the LDAP service provider - private static final String INITIAL_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory"; - - private static final String PROPERTY_NAME_CONNECTION_POOL = "com.sun.jndi.ldap.connect.pool"; - private static final String PROPERTY_NAME_CONNECT_TIMEOUT = "com.sun.jndi.ldap.connect.timeout"; - private static final String PROPERTY_NAME_READ_TIMEOUT = "com.sun.jndi.ldap.read.timeout"; - - /** - * The context for the LDAP server. This is the connection that is built - * from the configuration attributes "ldapHost", - * "principal" and "credentials". - */ - private LdapContext ldapContext; - // The schedule for retry attempts - private RetrySchedule schedule = null; - private LdapRepositoryConfiguration ldapConfiguration; + private String filterTemplate; + private LDAPConnectionPool ldapConnectionPool; @Inject public ReadOnlyLDAPUsersDAO() { @@ -91,8 +70,6 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { /** * Extracts the parameters required by the repository instance from the * James server configuration data. The fields extracted include - * {@link LdapRepositoryConfiguration#ldapHost}, {@link LdapRepositoryConfiguration#userIdAttribute}, {@link LdapRepositoryConfiguration#userBase}, - * {@link LdapRepositoryConfiguration#principal}, {@link LdapRepositoryConfiguration#credentials} and {@link LdapRepositoryConfiguration#restriction}. * * @param configuration * An encapsulation of the James server configuration data. @@ -104,11 +81,6 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { public void configure(LdapRepositoryConfiguration configuration) { ldapConfiguration = configuration; - - schedule = new DoublingRetrySchedule( - configuration.getRetryStartInterval(), - configuration.getRetryMaxInterval(), - configuration.getScale()); } /** @@ -120,63 +92,34 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { * specified LDAP host. */ public void init() throws Exception { + if (LOGGER.isDebugEnabled()) { LOGGER.debug(this.getClass().getName() + ".init()" + '\n' + "LDAP host: " + ldapConfiguration.getLdapHost() + '\n' + "User baseDN: " + ldapConfiguration.getUserBase() + '\n' + "userIdAttribute: " + ldapConfiguration.getUserIdAttribute() + '\n' + "Group restriction: " + ldapConfiguration.getRestriction() - + '\n' + "UseConnectionPool: " + ldapConfiguration.useConnectionPool() + '\n' + "connectionTimeout: " + + '\n' + "connectionTimeout: " + ldapConfiguration.getConnectionTimeout() + '\n' + "readTimeout: " + ldapConfiguration.getReadTimeout() - + '\n' + "retrySchedule: " + schedule + '\n' + "maxRetries: " + ldapConfiguration.getMaxRetries() + '\n'); + + '\n' + "maxRetries: " + ldapConfiguration.getMaxRetries() + '\n'); } - // Setup the initial LDAP context - updateLdapContext(); - } - - protected void updateLdapContext() throws NamingException { - ldapContext = computeLdapContext(); - } + filterTemplate = "(&({0}={1})(objectClass={2})" + StringUtils.defaultString(ldapConfiguration.getFilter(), "") + ")"; - /** - * Answers a new LDAP/JNDI context using the specified user credentials. - * - * @return an LDAP directory context - * @throws NamingException - * Propagated from underlying LDAP communication API. - */ - protected LdapContext computeLdapContext() throws NamingException { - return new RetryingLdapContext(schedule, ldapConfiguration.getMaxRetries()) { + LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions(); + connectionOptions.setConnectTimeoutMillis(ldapConfiguration.getConnectionTimeout()); + connectionOptions.setResponseTimeoutMillis(ldapConfiguration.getReadTimeout()); - @Override - public Context newDelegate() throws NamingException { - return new InitialLdapContext(getContextEnvironment(), null); - } - }; + URI uri = new URI(ldapConfiguration.getLdapHost()); + SocketFactory socketFactory = null; + LDAPConnection ldapConnection = new LDAPConnection(socketFactory, connectionOptions, uri.getHost(), uri.getPort(), ldapConfiguration.getPrincipal(), ldapConfiguration.getCredentials()); + ldapConnectionPool = new LDAPConnectionPool(ldapConnection, 4); + // TODO implement retries } - protected Properties getContextEnvironment() { - Properties props = new Properties(); - props.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY); - props.put(Context.PROVIDER_URL, Optional.ofNullable(ldapConfiguration.getLdapHost()) - .orElse("")); - if (Strings.isNullOrEmpty(ldapConfiguration.getCredentials())) { - props.put(Context.SECURITY_AUTHENTICATION, LdapConstants.SECURITY_AUTHENTICATION_NONE); - } else { - props.put(Context.SECURITY_AUTHENTICATION, LdapConstants.SECURITY_AUTHENTICATION_SIMPLE); - props.put(Context.SECURITY_PRINCIPAL, Optional.ofNullable(ldapConfiguration.getPrincipal()) - .orElse("")); - props.put(Context.SECURITY_CREDENTIALS, ldapConfiguration.getCredentials()); - } - // The following properties are specific to com.sun.jndi.ldap.LdapCtxFactory - props.put(PROPERTY_NAME_CONNECTION_POOL, String.valueOf(ldapConfiguration.useConnectionPool())); - if (ldapConfiguration.getConnectionTimeout() > -1) { - props.put(PROPERTY_NAME_CONNECT_TIMEOUT, String.valueOf(ldapConfiguration.getConnectionTimeout())); - } - if (ldapConfiguration.getReadTimeout() > -1) { - props.put(PROPERTY_NAME_READ_TIMEOUT, Integer.toString(ldapConfiguration.getReadTimeout())); - } - return props; + @PreDestroy + void dispose() { + ldapConnectionPool.close(); } + /** * Indicates if the user with the specified DN can be found in the group * membership map-as encapsulated by the specified parameter map. @@ -209,29 +152,20 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { return result; } - /** - * Gets all the user entities taken from the LDAP server, as taken from the - * search-context given by the value of the attribute {@link LdapRepositoryConfiguration#userBase}. - * - * @return A set containing all the relevant users found in the LDAP - * directory. - * @throws NamingException - * Propagated from the LDAP communication layer. - */ - private Set<String> getAllUsersFromLDAP() throws NamingException { - Set<String> result = new HashSet<>(); - - SearchControls sc = new SearchControls(); - sc.setSearchScope(SearchControls.SUBTREE_SCOPE); - sc.setReturningAttributes(new String[] { "distinguishedName" }); - NamingEnumeration<SearchResult> sr = ldapContext.search(ldapConfiguration.getUserBase(), "(objectClass=" - + ldapConfiguration.getUserObjectClass() + ")", sc); - while (sr.hasMore()) { - SearchResult r = sr.next(); - result.add(r.getNameInNamespace()); - } + private Set<String> getAllUsersFromLDAP() throws LDAPException { + LDAPConnection connection = ldapConnectionPool.getConnection(); + try { + SearchResult searchResult = connection.search(ldapConfiguration.getUserBase(), + SearchScope.SUB, + filterTemplate); - return result; + return searchResult.getSearchEntries() + .stream() + .map(entry -> entry.getObjectClassAttribute().getName()) + .collect(Guavate.toImmutableSet()); + } finally { + ldapConnectionPool.releaseConnection(connection); + } } /** @@ -245,11 +179,10 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { * is to be extracted from the LDAP repository. * @return A collection of {@link ReadOnlyLDAPUser}s as taken from the LDAP * server. - * @throws NamingException + * @throws LDAPException * Propagated from the underlying LDAP communication layer. */ - private Collection<ReadOnlyLDAPUser> buildUserCollection(Collection<String> userDNs) - throws NamingException { + private Collection<ReadOnlyLDAPUser> buildUserCollection(Collection<String> userDNs) throws LDAPException { List<ReadOnlyLDAPUser> results = new ArrayList<>(); for (String userDN : userDNs) { @@ -260,42 +193,34 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { return results; } - /** - * For a given name, this method makes ldap search in userBase with filter {@link LdapRepositoryConfiguration#userIdAttribute}=name - * and objectClass={@link LdapRepositoryConfiguration#userObjectClass} and builds {@link User} based on search result. - * - * @param name - * The userId which should be value of the field {@link LdapRepositoryConfiguration#userIdAttribute} - * @return A {@link ReadOnlyLDAPUser} instance which is initialized with the - * userId of this user and ldap connection information with which - * the user was searched. Return null if such a user was not found. - * @throws NamingException - * Propagated by the underlying LDAP communication layer. - */ - private ReadOnlyLDAPUser searchAndBuildUser(Username name) throws NamingException { - SearchControls sc = new SearchControls(); - sc.setSearchScope(SearchControls.SUBTREE_SCOPE); - sc.setReturningAttributes(new String[] { ldapConfiguration.getUserIdAttribute() }); - sc.setCountLimit(1); - - String filterTemplate = "(&({0}={1})(objectClass={2})" + - StringUtils.defaultString(ldapConfiguration.getFilter(), "") + - ")"; - - String sanitizedFilter = FilterEncoder.format( - filterTemplate, - ldapConfiguration.getUserIdAttribute(), - name.asString(), - ldapConfiguration.getUserObjectClass()); - - NamingEnumeration<SearchResult> sr = ldapContext.search(ldapConfiguration.getUserBase(), sanitizedFilter, sc); - - if (!sr.hasMore()) { - return null; + private ReadOnlyLDAPUser searchAndBuildUser(Username name) throws LDAPException { + LDAPConnection connection = ldapConnectionPool.getConnection(); + try { + String sanitizedFilter = FilterEncoder.format( + filterTemplate, + ldapConfiguration.getUserIdAttribute(), + name.asString(), + ldapConfiguration.getUserObjectClass()); + + SearchResult searchResult = connection.search(ldapConfiguration.getUserBase(), + SearchScope.SUB, + sanitizedFilter, + ldapConfiguration.getUserIdAttribute()); + + return searchResult.getSearchEntries() + .stream() + .map(entry -> new ReadOnlyLDAPUser( + Username.of(entry.getAttribute(ldapConfiguration.getUserIdAttribute()).getName()), + entry.getDN(), + ldapConnectionPool)) + .findFirst() + .orElse(null); + } finally { + ldapConnectionPool.releaseConnection(connection); } - SearchResult r = sr.next(); - Attribute userName = r.getAttributes().get(ldapConfiguration.getUserIdAttribute()); + /* + TODO implement restrictions if (!ldapConfiguration.getRestriction().isActivated() || userInGroupsMembershipList(r.getNameInNamespace(), ldapConfiguration.getRestriction().getGroupMembershipLists(ldapContext))) { @@ -303,31 +228,20 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { } return null; + */ } - /** - * Given a userDN, this method retrieves the user attributes from the LDAP - * server, so as to extract the items that are of interest to James. - * Specifically it extracts the userId, which is extracted from the LDAP - * attribute whose name is given by the value of the field - * {@link LdapRepositoryConfiguration#userIdAttribute}. - * - * @param userDN - * The distinguished-name of the user whose details are to be - * extracted from the LDAP repository. - * @return A {@link ReadOnlyLDAPUser} instance which is initialized with the - * userId of this user and ldap connection information with which - * the userDN and attributes were obtained. - * @throws NamingException - * Propagated by the underlying LDAP communication layer. - */ - private Optional<ReadOnlyLDAPUser> buildUser(String userDN) throws NamingException { - Attributes userAttributes = ldapContext.getAttributes(userDN); - Optional<Attribute> userName = Optional.ofNullable(userAttributes.get(ldapConfiguration.getUserIdAttribute())); - return userName - .map(Throwing.<Attribute, String>function(u -> u.get().toString()).sneakyThrow()) - .map(Username::of) - .map(username -> new ReadOnlyLDAPUser(username, userDN, ldapContext)); + private Optional<ReadOnlyLDAPUser> buildUser(String userDN) throws LDAPException { + LDAPConnection connection = ldapConnectionPool.getConnection(); + try { + SearchResultEntry userAttributes = connection.getEntry(userDN); + Optional<String> userName = Optional.ofNullable(userAttributes.getAttributeValue(ldapConfiguration.getUserIdAttribute())); + return userName + .map(Username::of) + .map(username -> new ReadOnlyLDAPUser(username, userDN, ldapConnectionPool)); + } finally { + ldapConnectionPool.releaseConnection(connection); + } } @Override @@ -342,7 +256,7 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { .map(Throwing.function(this::buildUser).sneakyThrow()) .flatMap(Optional::stream) .count()); - } catch (NamingException e) { + } catch (LDAPException e) { throw new UsersRepositoryException("Unable to retrieve user count from ldap", e); } } @@ -351,7 +265,7 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { public Optional<User> getUserByName(Username name) throws UsersRepositoryException { try { return Optional.ofNullable(searchAndBuildUser(name)); - } catch (NamingException e) { + } catch (LDAPException e) { throw new UsersRepositoryException("Unable to retrieve user from ldap", e); } } @@ -362,19 +276,22 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { return buildUserCollection(getValidUsers()) .stream() .map(ReadOnlyLDAPUser::getUserName) - .collect(Guavate.toImmutableList()) .iterator(); - } catch (NamingException namingException) { + } catch (LDAPException namingException) { throw new UsersRepositoryException( "Unable to retrieve users list from LDAP due to unknown naming error.", namingException); } } - private Collection<String> getValidUsers() throws NamingException { - Set<String> userDNs = getAllUsersFromLDAP(); - Collection<String> validUserDNs; + private Collection<String> getValidUsers() throws LDAPException { + return getAllUsersFromLDAP(); + /* + TODO Implement restrictions + */ + /* + Collection<String> validUserDNs; if (ldapConfiguration.getRestriction().isActivated()) { Map<String, Collection<String>> groupMembershipList = ldapConfiguration.getRestriction() .getGroupMembershipLists(ldapContext); @@ -392,6 +309,7 @@ public class ReadOnlyLDAPUsersDAO implements UsersDAO, Configurable { validUserDNs = userDNs; } return validUserDNs; + */ } @Override diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/api/LdapConstants.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/api/LdapConstants.java deleted file mode 100644 index 1227bca..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/api/LdapConstants.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.james.user.ldap.api; - -/** - * <code>LdapConstants</code> - */ -public interface LdapConstants { - - // The authentication mechanisms for the provider to use - String SECURITY_AUTHENTICATION_NONE = "none"; - String SECURITY_AUTHENTICATION_SIMPLE = "simple"; - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/DoublingRetrySchedule.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/DoublingRetrySchedule.java deleted file mode 100644 index f9e435e..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/DoublingRetrySchedule.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.james.user.ldap.retry; - -import org.apache.james.user.ldap.retry.api.RetrySchedule; - - -/** - * <code>DoublingRetrySchedule</code> is an implementation of <code>RetrySchedule</code> that - * returns the lesser of an interval that is double the proceeding interval and the maximum interval. - * - * <p>When the initial interval is 0, the next interval is 1. - * - * <p>A multiplier can be specified to scale the results. - */ -/** - * <code>DoublingRetrySchedule</code> - */ -public class DoublingRetrySchedule implements RetrySchedule { - - private long startInterval = 0; - private long maxInterval = 0; - private long multiplier = 1; - - /** - * Creates a new instance of DoublingRetrySchedule. - * - */ - private DoublingRetrySchedule() { - } - - /** - * Creates a new instance of DoublingRetrySchedule. - * - * @param startInterval - * The interval for an index of 0 - * @param maxInterval - * The maximum interval for any index - */ - public DoublingRetrySchedule(long startInterval, long maxInterval) { - this(startInterval, maxInterval, 1); - } - - /** - * Creates a new instance of DoublingRetrySchedule. - * - * @param startInterval - * The interval for an index of 0 - * @param maxInterval - * The maximum interval for any index - * @param multiplier - * The multiplier to apply to the result - */ - public DoublingRetrySchedule(long startInterval, long maxInterval, int multiplier) { - this(); - this.startInterval = Math.max(0, startInterval); - this.maxInterval = Math.max(0, maxInterval); - this.multiplier = Math.max(1, multiplier); - } - - @Override - public long getInterval(int index) { - if (startInterval > 0) { - return getInterval(index, startInterval); - } - return index == 0 ? 0 : getInterval(index - 1, 1); - } - - private long getInterval(int index, long startInterval) { - return multiplier * Math.min((long) (startInterval * Math.pow(2, index)), maxInterval); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("DoublingRetrySchedule [startInterval=").append(startInterval).append( - ", maxInterval=").append(maxInterval).append(", multiplier=").append(multiplier).append("]"); - return builder.toString(); - } - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/ExceptionRetryHandler.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/ExceptionRetryHandler.java deleted file mode 100644 index 218048b..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/ExceptionRetryHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.james.user.ldap.retry; - -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetryHandler; -import org.apache.james.user.ldap.retry.api.RetrySchedule; - -/** - * Abstract class <code>ExceptionRetryHandler</code> retries the behaviour defined in abstract method - * <code>operation()</code> implemented in a concrete subclass when nominated subclasses of <code>Exception</code> - * are thrown. The intervals between retries are defined by a <code>RetrySchedule</code>. - * <p> - * Concrete subclasses are proxies that forward work via the <code>operation()</code> to a delegate - * instance for which retry behaviour is required. Both the proxy and the delegate implement the - * same interfaces. - * <p> - * The method stubs required to perform the proxy call via <code>operation()</code> may be generated by many means, - * including explicit code, a <code>DynamicProxy</code> and compile time aspect injection. - * - * @see org.apache.james.user.ldap.retry.naming.RetryingContext - */ -public abstract class ExceptionRetryHandler implements RetryHandler { - - private Class<?>[] exceptionClasses = null; - - private ExceptionRetryingProxy proxy = null; - private RetrySchedule schedule; - private int maxRetries = 0; - - /** - * Creates a new instance of ExceptionRetryHandler. - * - */ - private ExceptionRetryHandler() { - super(); - } - - - /** - * Creates a new instance of ExceptionRetryHandler. - * - * @param exceptionClasses - * @param proxy - * @param maxRetries - */ - public ExceptionRetryHandler(Class<?>[] exceptionClasses, ExceptionRetryingProxy proxy, RetrySchedule schedule, int maxRetries) { - this(); - this.exceptionClasses = exceptionClasses; - this.proxy = proxy; - this.schedule = schedule; - this.maxRetries = maxRetries; - } - - @Override - public Object perform() throws Exception { - boolean success = false; - Object result = null; - int retryCount = 0; - while (!success) { - try { - if (retryCount > 0) { - proxy.resetDelegate(); - } - result = operation(); - success = true; - - } catch (Exception ex) { - if (retryCount >= maxRetries || !isRetryable(ex)) { - throw ex; - } - postFailure(ex, retryCount); - try { - Thread.sleep(getRetryInterval(retryCount)); - } catch (InterruptedException ex1) { - // no-op - } - retryCount = maxRetries < 0 ? maxRetries - : retryCount + 1; - } - } - return result; - } - - /** - * @param ex - * The <code>Throwable</code> to test - * - * @return true if the array of exception classes contains <strong>ex</strong> - */ - private boolean isRetryable(Throwable ex) { - boolean isRetryable = false; - for (int i = 0; !isRetryable && i < exceptionClasses.length; i++) { - isRetryable = exceptionClasses[i].isInstance(ex); - } - return isRetryable; - } - - @Override - public void postFailure(Exception ex, int retryCount) { - // no-op - } - - @Override - public abstract Object operation() throws Exception; - - /** - * @return the retryInterval - */ - public long getRetryInterval(int retryCount) { - return schedule.getInterval(retryCount); - } -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/ExceptionRetryingProxy.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/ExceptionRetryingProxy.java deleted file mode 100644 index fb41fe9..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/ExceptionRetryingProxy.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.james.user.ldap.retry.api; - - -/** - * <code>ExceptionRetryingProxy</code> defines the behaviour for a - * proxy that can retry <codeException</code> and its subclasses. - */ -public interface ExceptionRetryingProxy { - /** - * @return a new instance that the proxy delegates to - * @throws Exception - */ - Object newDelegate() throws Exception; - - /** - * @return the current instance of the proxy delegate - * @throws Exception - */ - Object getDelegate(); - - /** - * Resets the delegate instance to a state from which it can perform the - * operations delegated to it. - * - * @throws Exception - */ - void resetDelegate() throws Exception; - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetryHandler.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetryHandler.java deleted file mode 100644 index 77f7e56..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetryHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.james.user.ldap.retry.api; - - -/** - * <code>RetryHandler</code> - */ -public interface RetryHandler { - - /** - * @return the result of invoking an operation - * @throws Exception - */ - Object perform() throws Exception; - - /** - * A hook invoked each time an operation fails if a retry is scheduled - * - * @param ex - * The <code>Exception</code> thrown when the operation was invoked - * @param retryCount - * The number of times - */ - void postFailure(Exception ex, int retryCount); - - /** - * Encapsulates desired behaviour - * @return The result of performing the behaviour - * @throws Exception - */ - Object operation() throws Exception; - -} \ No newline at end of file diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetrySchedule.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetrySchedule.java deleted file mode 100644 index 6607925..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/api/RetrySchedule.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.james.user.ldap.retry.api; - -/** - * <code>RetrySchedule</code> - */ -public interface RetrySchedule { - /** - * Returns the interval time in milliseconds at the specified zero based index. - * - * @param index - * @return - */ - long getInterval(int index); - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/LoggingRetryHandler.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/LoggingRetryHandler.java deleted file mode 100644 index e9af6f4..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/LoggingRetryHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming; - -import javax.naming.NamingException; - -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Abstract class <code>LoggingRetryHandler</code> implements logging of failures - */ -public abstract class LoggingRetryHandler extends NamingExceptionRetryHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(LoggingRetryHandler.class); - - /** - * Creates a new instance of LoggingRetryHandler. - * - * @param exceptionClasses - * @param proxy - * @param maxRetries - */ - public LoggingRetryHandler(Class<?>[] exceptionClasses, ExceptionRetryingProxy proxy, - RetrySchedule schedule, int maxRetries) { - super(exceptionClasses, proxy, schedule, maxRetries); - } - - @Override - public void postFailure(NamingException ex, int retryCount) { - super.postFailure(ex, retryCount); - LOGGER.info("Retry failure. Retrying in {} seconds", getRetryInterval(retryCount) / 1000, ex); - } - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandler.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandler.java deleted file mode 100644 index 368b5d2..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming; - -import javax.naming.NamingException; - -import org.apache.james.user.ldap.retry.ExceptionRetryHandler; -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetrySchedule; - -/** - * Abstract class <code>NamingExceptionRetryHandler</code> narrows the set of Exceptions throwable - * by <code>perform</code> to <code>NamingException</code> and its subclasses. - * <p><code>RuntimeException</code>s are <strong>not</strong> retried. - * - * @see org.apache.james.user.ldap.retry.ExceptionRetryHandler - * - */ -public abstract class NamingExceptionRetryHandler extends ExceptionRetryHandler { - - /** - * Creates a new instance of NamingExceptionRetryHandler. - * - * @param exceptionClasses - * @param proxy - * @param schedule - * @param maxRetries - */ - public NamingExceptionRetryHandler(Class<?>[] exceptionClasses, ExceptionRetryingProxy proxy, - RetrySchedule schedule, int maxRetries) { - super(exceptionClasses, proxy, schedule, maxRetries); - } - - @Override - public Object perform() throws NamingException { - try { - return super.perform(); - } catch (RuntimeException ex) { - throw ex; - } catch (Exception ex) { - // Should only ever be a NamingException - throw ((NamingException) ex); - } - } - - @Override - public void postFailure(Exception ex, int retryCount) { - postFailure(((NamingException) ex), retryCount); - } - - public void postFailure(NamingException ex, int retryCount) { - // no-op - } -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/RetryingContext.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/RetryingContext.java deleted file mode 100644 index 65bbd1a..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/RetryingContext.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming; - -import java.util.Hashtable; - -import javax.naming.Binding; -import javax.naming.CommunicationException; -import javax.naming.Context; -import javax.naming.Name; -import javax.naming.NameClassPair; -import javax.naming.NameParser; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.NoInitialContextException; -import javax.naming.ServiceUnavailableException; - -import org.apache.james.user.ldap.retry.ExceptionRetryHandler; -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetrySchedule; - -/** - * <code>RetryingContext</code> retries the methods defined by <code>javax.naming.Context</code> - * according to the specified schedule. - * - * @see ExceptionRetryHandler - * @see org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy - * @see javax.naming.Context - */ -public abstract class RetryingContext implements Context, ExceptionRetryingProxy { - - public static final Class<?>[] DEFAULT_EXCEPTION_CLASSES = new Class<?>[] { - CommunicationException.class, - ServiceUnavailableException.class, - NoInitialContextException.class }; - - private Context delegate = null; - private RetrySchedule schedule = null; - private int maxRetries = 0; - - /** - * Creates a new instance of RetryingContext. - * - * @throws NamingException - * - */ - private RetryingContext() { - super(); - } - - /** - * Creates a new instance of RetryingContext using the default exception - * classes thrown when an external interruption to services on which we - * depend occurs. - * - * @param schedule - * @param maxRetries - * @throws NamingException - */ - public RetryingContext(RetrySchedule schedule, int maxRetries) - throws NamingException { - this(DEFAULT_EXCEPTION_CLASSES, schedule, maxRetries); - } - - /** - * Creates a new instance of RetryingContext. - * - * @param exceptionClasses - * @param schedule - * @param maxRetries - * @throws NamingException - */ - public RetryingContext(Class<?>[] exceptionClasses, RetrySchedule schedule, int maxRetries) - throws NamingException { - this(); - this.schedule = schedule; - this.maxRetries = maxRetries; - this.delegate = (Context) new LoggingRetryHandler(exceptionClasses, this, - this.schedule, this.maxRetries) { - - @Override - public Object operation() throws Exception { - return newDelegate(); - } - }.perform(); - } - - @Override - public Object addToEnvironment(final String propName, final Object propVal) - throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().addToEnvironment(propName, propVal); - } - }.perform(); - } - - @Override - public void bind(final Name name, final Object obj) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().bind(name, obj); - return null; - } - }.perform(); - } - - @Override - public void bind(final String name, final Object obj) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().bind(name, obj); - return null; - } - }.perform(); - } - - @Override - public void close() throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().close(); - return null; - } - }.perform(); - } - - @Override - public Name composeName(final Name name, final Name prefix) throws NamingException { - return (Name) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().composeName(name, prefix); - } - }.perform(); - } - - @Override - public String composeName(final String name, final String prefix) throws NamingException { - return (String) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().composeName(name, prefix); - } - }.perform(); - } - - @Override - public Context createSubcontext(final Name name) throws NamingException { - final Context context = getDelegate(); - return new RetryingContext(getSchedule(), getMaxRetries()) { - - @Override - public Context newDelegate() throws NamingException { - return context.createSubcontext(name); - } - }; - } - - @Override - public Context createSubcontext(final String name) throws NamingException { - final Context context = getDelegate(); - return new RetryingContext(getSchedule(), getMaxRetries()) { - - @Override - public Context newDelegate() throws NamingException { - return context.createSubcontext(name); - } - }; - } - - @Override - public void destroySubcontext(final Name name) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().destroySubcontext(name); - return null; - } - }.perform(); - } - - @Override - public void destroySubcontext(final String name) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().destroySubcontext(name); - return null; - } - }.perform(); - } - - @Override - public Hashtable<?, ?> getEnvironment() throws NamingException { - return (Hashtable<?, ?>) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - return getDelegate().getEnvironment(); - } - }.perform(); - } - - @Override - public String getNameInNamespace() throws NamingException { - return (String) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().getNameInNamespace(); - } - }.perform(); - } - - @Override - public NameParser getNameParser(final Name name) throws NamingException { - return (NameParser) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().getNameParser(name); - } - }.perform(); - } - - @Override - public NameParser getNameParser(final String name) throws NamingException { - return (NameParser) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().getNameParser(name); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException { - return (NamingEnumeration<NameClassPair>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - return getDelegate().list(name); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<NameClassPair> list(final String name) throws NamingException { - return (NamingEnumeration<NameClassPair>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - return getDelegate().list(name); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException { - return (NamingEnumeration<Binding>) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, - this, schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - return getDelegate().listBindings(name); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<Binding> listBindings(final String name) throws NamingException { - return (NamingEnumeration<Binding>) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, - this, schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - return getDelegate().listBindings(name); - } - }.perform(); - } - - @Override - public Object lookup(final Name name) throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().lookup(name); - } - }.perform(); - } - - @Override - public Object lookup(final String name) throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().lookup(name); - } - }.perform(); - } - - @Override - public Object lookupLink(final Name name) throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().lookupLink(name); - } - }.perform(); - } - - @Override - public Object lookupLink(final String name) throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().lookupLink(name); - } - }.perform(); - } - - @Override - public void rebind(final Name name, final Object obj) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().rebind(name, obj); - return null; - } - }.perform(); - } - - @Override - public void rebind(final String name, final Object obj) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().rebind(name, obj); - return null; - } - }.perform(); - } - - @Override - public Object removeFromEnvironment(final String propName) throws NamingException { - return new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - return getDelegate().removeFromEnvironment(propName); - } - }.perform(); - } - - @Override - public void rename(final Name oldName, final Name newName) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().rename(oldName, newName); - return null; - } - }.perform(); - } - - @Override - public void rename(final String oldName, final String newName) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().rename(oldName, newName); - return null; - } - }.perform(); - } - - @Override - public void unbind(final Name name) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries - ) { - - @Override - public Object operation() throws NamingException { - getDelegate().unbind(name); - return null; - } - }.perform(); - } - - @Override - public void unbind(final String name) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, schedule, maxRetries) { - - @Override - public Object operation() throws NamingException { - getDelegate().unbind(name); - return null; - } - }.perform(); - } - - @Override - public Context getDelegate() { - return delegate; - } - - - @Override - public void resetDelegate() throws Exception { - if (null != delegate) { - delegate.close(); - } - delegate = (Context)newDelegate(); - } - - /** - * @return the schedule - */ - public RetrySchedule getSchedule() { - return schedule; - } - - /** - * @return the maxRetries - */ - public int getMaxRetries() { - return maxRetries; - } - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/directory/RetryingDirContext.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/directory/RetryingDirContext.java deleted file mode 100644 index 72c1679..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/directory/RetryingDirContext.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming.directory; - -import javax.naming.Context; -import javax.naming.Name; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.ModificationItem; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; - -import org.apache.james.user.ldap.retry.ExceptionRetryHandler; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.apache.james.user.ldap.retry.naming.LoggingRetryHandler; -import org.apache.james.user.ldap.retry.naming.RetryingContext; - -/** - * <code>RetryingDirContext</code> retries the methods defined by <code>javax.naming.directory.DirContext</code> - * according to the specified schedule. - * - * @see ExceptionRetryHandler - * @see org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy - * @see javax.naming.directory.DirContext - */ -public abstract class RetryingDirContext extends RetryingContext implements DirContext { - - - /** - * Creates a new instance of RetryingDirContext. - * - * @param schedule - * @param maxRetries - * @throws NamingException - */ - public RetryingDirContext(RetrySchedule schedule, int maxRetries) - throws NamingException { - super(schedule, maxRetries); - } - - @Override - public void bind(final Name name, final Object obj, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).bind(name, obj, attrs); - return null; - } - }.perform(); - } - - @Override - public void bind(final String name, final Object obj, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).bind(name, obj, attrs); - return null; - } - }.perform(); - } - - @Override - public DirContext createSubcontext(final Name name, final Attributes attrs) - throws NamingException { - return (DirContext) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).createSubcontext(name, attrs); - } - }.perform(); - } - - @Override - public DirContext createSubcontext(final String name, final Attributes attrs) - throws NamingException { - return (DirContext) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).createSubcontext(name, attrs); - } - }.perform(); - } - - @Override - public Attributes getAttributes(final Name name) throws NamingException { - return (Attributes) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).getAttributes(name); - } - }.perform(); - } - - @Override - public Attributes getAttributes(final String name) throws NamingException { - return (Attributes) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).getAttributes(name); - } - }.perform(); - } - - @Override - public Attributes getAttributes(final Name name, final String[] attrIds) throws NamingException { - return (Attributes) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).getAttributes(name, attrIds); - } - }.perform(); - } - - @Override - public Attributes getAttributes(final String name, final String[] attrIds) - throws NamingException { - return (Attributes) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, - getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).getAttributes(name, attrIds); - } - }.perform(); - } - - @Override - public DirContext getSchema(final Name name) throws NamingException { - final Context context = getDelegate(); - return new RetryingDirContext(getSchedule(), getMaxRetries()) { - - @Override - public DirContext newDelegate() throws NamingException { - return ((DirContext) context).getSchema(name); - } - }; - } - - @Override - public DirContext getSchema(final String name) throws NamingException { - final Context context = getDelegate(); - return new RetryingDirContext(getSchedule(), getMaxRetries()) { - - @Override - public DirContext newDelegate() throws NamingException { - return ((DirContext) context).getSchema(name); - } - }; - } - - @Override - public DirContext getSchemaClassDefinition(final Name name) throws NamingException { - final Context context = getDelegate(); - return new RetryingDirContext(getSchedule(), getMaxRetries()) { - - @Override - public DirContext newDelegate() throws NamingException { - return ((DirContext) context).getSchemaClassDefinition(name); - } - }; - } - - @Override - public DirContext getSchemaClassDefinition(final String name) throws NamingException { - final Context context = getDelegate(); - return new RetryingDirContext(getSchedule(), getMaxRetries()) { - - @Override - public DirContext newDelegate() throws NamingException { - return ((DirContext) context).getSchemaClassDefinition(name); - } - }; - } - - @Override - public void modifyAttributes(final Name name, final ModificationItem[] mods) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).modifyAttributes(name, mods); - return null; - } - }.perform(); - } - - @Override - public void modifyAttributes(final String name, final ModificationItem[] mods) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).modifyAttributes(name, mods); - return null; - } - }.perform(); - } - - @Override - public void modifyAttributes(final Name name, final int modOp, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).modifyAttributes(name, modOp, attrs); - return null; - } - }.perform(); - } - - @Override - public void modifyAttributes(final String name, final int modOp, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).modifyAttributes(name, modOp, attrs); - return null; - } - }.perform(); - } - - @Override - public void rebind(final Name name, final Object obj, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).rebind(name, obj, attrs); - return null; - } - }.perform(); - } - - @Override - public void rebind(final String name, final Object obj, final Attributes attrs) - throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - ((DirContext) getDelegate()).rebind(name, obj, attrs); - return null; - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final Name name, - final Attributes matchingAttributes) - throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, matchingAttributes); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final String name, - final Attributes matchingAttributes) - throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, matchingAttributes); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final Name name, - final Attributes matchingAttributes, - String[] attributesToReturn) throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, matchingAttributes); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final String name, - final Attributes matchingAttributes, - final String[] attributesToReturn) throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, matchingAttributes, - attributesToReturn); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final Name name, final String filter, - final SearchControls cons) - throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, filter, cons); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final String name, final String filter, - final SearchControls cons) - throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, filter, cons); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final Name name, final String filterExpr, - final Object[] filterArgs, final SearchControls cons) throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, filterExpr, filterArgs, cons); - } - }.perform(); - } - - @SuppressWarnings("unchecked") - @Override - public NamingEnumeration<SearchResult> search(final String name, final String filterExpr, - final Object[] filterArgs, final SearchControls cons) throws NamingException { - return (NamingEnumeration<SearchResult>) new LoggingRetryHandler( - DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - - @Override - public Object operation() throws NamingException { - return ((DirContext) getDelegate()).search(name, filterExpr, filterArgs, cons); - } - }.perform(); - } - -} diff --git a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/ldap/RetryingLdapContext.java b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/ldap/RetryingLdapContext.java deleted file mode 100644 index 0355c09..0000000 --- a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/retry/naming/ldap/RetryingLdapContext.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming.ldap; - -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.ldap.Control; -import javax.naming.ldap.ExtendedRequest; -import javax.naming.ldap.ExtendedResponse; -import javax.naming.ldap.LdapContext; - -import org.apache.james.user.ldap.retry.ExceptionRetryHandler; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.apache.james.user.ldap.retry.naming.LoggingRetryHandler; -import org.apache.james.user.ldap.retry.naming.directory.RetryingDirContext; - -/** - * <code>RetryingLdapContext</code> retries the methods defined by <code>javax.naming.ldap.LdapContext</code> - * according to the specified schedule. - * - * @see ExceptionRetryHandler - * @see org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy - * @see javax.naming.ldap.LdapContext - */ -public abstract class RetryingLdapContext extends RetryingDirContext implements LdapContext { - - /** - * Creates a new instance of RetryingLdapContext. - * - * @param maxRetries - * @throws NamingException - */ - public RetryingLdapContext(RetrySchedule schedule, int maxRetries) throws NamingException { - super(schedule, maxRetries); - } - - @Override - public ExtendedResponse extendedOperation(final ExtendedRequest request) throws NamingException { - return (ExtendedResponse) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - return ((LdapContext) getDelegate()).extendedOperation(request); - } - }.perform(); - } - - @Override - public Control[] getConnectControls() throws NamingException { - return (Control[]) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - return ((LdapContext) getDelegate()).getConnectControls(); - } - }.perform(); - } - - @Override - public Control[] getRequestControls() throws NamingException { - return (Control[]) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - return ((LdapContext) getDelegate()).getRequestControls(); - } - }.perform(); - } - - @Override - public Control[] getResponseControls() throws NamingException { - return (Control[]) new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - return ((LdapContext) getDelegate()).getResponseControls(); - } - }.perform(); - } - - @Override - public LdapContext newInstance(final Control[] requestControls) throws NamingException { - final Context context = getDelegate(); - return new RetryingLdapContext(getSchedule(), getMaxRetries()) { - - @Override - public Context newDelegate() throws NamingException { - return ((LdapContext) context).newInstance(requestControls); - } - }; - } - - @Override - public void reconnect(final Control[] connCtls) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - ((LdapContext) getDelegate()).reconnect(connCtls); - return null; - } - }.perform(); - } - - @Override - public void setRequestControls(final Control[] requestControls) throws NamingException { - new LoggingRetryHandler(DEFAULT_EXCEPTION_CLASSES, this, getSchedule(), getMaxRetries()) { - @Override - public Object operation() throws NamingException { - ((LdapContext) getDelegate()).setRequestControls(requestControls); - return null; - } - }.perform(); - } - -} diff --git a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/DoublingRetryScheduleTest.java b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/DoublingRetryScheduleTest.java deleted file mode 100644 index 7ec732c..0000000 --- a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/DoublingRetryScheduleTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.james.user.ldap.retry; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.junit.jupiter.api.Test; - -class DoublingRetryScheduleTest { - - @Test - void testDoublingRetrySchedule() { - assertThat(RetrySchedule.class.isAssignableFrom(DoublingRetrySchedule.class)).isTrue(); - assertThat(new DoublingRetrySchedule(0, 0).getInterval(0)).isEqualTo(0); - assertThat(new DoublingRetrySchedule(-1, -1).getInterval(0)).isEqualTo(0); - assertThat(new DoublingRetrySchedule(-1, 0).getInterval(0)).isEqualTo(0); - assertThat(new DoublingRetrySchedule(0, -1).getInterval(0)).isEqualTo(0); - } - - @Test - void testGetInterval() { - assertThat(new DoublingRetrySchedule(0, 8).getInterval(0)).isEqualTo(0); - assertThat(new DoublingRetrySchedule(0, 8).getInterval(1)).isEqualTo(1); - assertThat(new DoublingRetrySchedule(0, 8).getInterval(2)).isEqualTo(2); - assertThat(new DoublingRetrySchedule(0, 8).getInterval(3)).isEqualTo(4); - assertThat(new DoublingRetrySchedule(0, 8).getInterval(4)).isEqualTo(8); - assertThat(new DoublingRetrySchedule(0, 8).getInterval(5)).isEqualTo(8); - - assertThat(new DoublingRetrySchedule(1, 8).getInterval(0)).isEqualTo(1); - assertThat(new DoublingRetrySchedule(1, 8).getInterval(1)).isEqualTo(2); - assertThat(new DoublingRetrySchedule(1, 8).getInterval(2)).isEqualTo(4); - assertThat(new DoublingRetrySchedule(1, 8).getInterval(3)).isEqualTo(8); - assertThat(new DoublingRetrySchedule(1, 8).getInterval(4)).isEqualTo(8); - - assertThat(new DoublingRetrySchedule(3, 12).getInterval(0)).isEqualTo(3); - assertThat(new DoublingRetrySchedule(3, 12).getInterval(1)).isEqualTo(6); - assertThat(new DoublingRetrySchedule(3, 12).getInterval(2)).isEqualTo(12); - assertThat(new DoublingRetrySchedule(3, 12).getInterval(3)).isEqualTo(12); - - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(0)).isEqualTo(0); - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(1)).isEqualTo(1000); - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(2)).isEqualTo(2000); - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(3)).isEqualTo(4000); - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(4)).isEqualTo(8000); - assertThat(new DoublingRetrySchedule(0, 8, 1000).getInterval(5)).isEqualTo(8000); - } - - @Test - void testToString() { - assertThat(new DoublingRetrySchedule(0, 1).toString()) - .isEqualTo("DoublingRetrySchedule [startInterval=0, maxInterval=1, multiplier=1]"); - } -} diff --git a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/ExceptionRetryHandlerTest.java b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/ExceptionRetryHandlerTest.java deleted file mode 100644 index f3d921d..0000000 --- a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/ExceptionRetryHandlerTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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.james.user.ldap.retry; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.ArrayList; -import java.util.List; - -import javax.naming.Context; - -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetryHandler; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class ExceptionRetryHandlerTest { - private static class TestRetryingProxy implements ExceptionRetryingProxy { - - @Override - public Context getDelegate() { - return null; - } - - @Override - public Context newDelegate() { - return null; - } - - @Override - public void resetDelegate() { - } - } - - private Class<?>[] exceptionClasses = null; - private ExceptionRetryingProxy proxy = null; - private RetrySchedule schedule = null; - - @BeforeEach - void setUp() { - exceptionClasses = new Class<?>[]{Exception.class}; - proxy = new TestRetryingProxy(); - schedule = i -> i; - } - - @Test - void testExceptionRetryHandler() { - assertThat(RetryHandler.class.isAssignableFrom(new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return null; - } - }.getClass())).isTrue(); - } - - @Test - void testPerform() throws Exception { - Object result = new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return "Hi!"; - } - }.perform(); - assertThat(result).isEqualTo("Hi!"); - - try { - new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() throws Exception { - throw new Exception(); - } - }.perform(); - } catch (Exception ex) { - // no-op - } - assertThat(result).isEqualTo("Hi!"); - } - - @Test - void testPostFailure() { - final List<Exception> results = new ArrayList<>(); - RetryHandler handler = new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 7) { - - @Override - public void postFailure(Exception ex, int retryCount) { - super.postFailure(ex, retryCount); - results.add(ex); - } - - @Override - public Object operation() throws Exception { - throw new Exception(); - } - }; - try { - handler.perform(); - } catch (Exception ex) { - // no-op - } - assertThat(results.size()).isEqualTo(7); - } - - @Test - void testOperation() throws Exception { - RetryHandler handler = new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return "Hi!"; - } - }; - assertThat(handler.operation()).isEqualTo("Hi!"); - } - - @Test - void testGetRetryInterval() { - ExceptionRetryHandler handler = new ExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return null; - } - }; - assertThat(handler.getRetryInterval(8)).isEqualTo(8); - } -} diff --git a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandlerTest.java b/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandlerTest.java deleted file mode 100644 index 59c8fb8..0000000 --- a/server/data/data-ldap/src/test/java/org/apache/james/user/ldap/retry/naming/NamingExceptionRetryHandlerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.james.user.ldap.retry.naming; - -import static org.assertj.core.api.Assertions.assertThat; - -import javax.naming.Context; -import javax.naming.NamingException; - -import org.apache.james.user.ldap.retry.api.ExceptionRetryingProxy; -import org.apache.james.user.ldap.retry.api.RetryHandler; -import org.apache.james.user.ldap.retry.api.RetrySchedule; -import org.junit.jupiter.api.Test; - -class NamingExceptionRetryHandlerTest { - - private static class TestRetryingProxy implements ExceptionRetryingProxy { - @Override - public Context getDelegate() { - return null; - } - - @Override - public Context newDelegate() { - return null; - } - - @Override - public void resetDelegate() { - } - } - - private static final Class<?>[] exceptionClasses = new Class<?>[]{NamingException.class}; - private static final ExceptionRetryingProxy proxy = new TestRetryingProxy(); - private static final RetrySchedule schedule = i -> i; - - @Test - void testExceptionRetryHandler() { - assertThat(RetryHandler.class.isAssignableFrom(new NamingExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return null; - } - }.getClass())).isTrue(); - } - - @Test - void testPerform() throws NamingException { - Object result = new NamingExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() { - return "Hi!"; - } - }.perform(); - assertThat(result).isEqualTo("Hi!"); - - try { - new NamingExceptionRetryHandler( - exceptionClasses, proxy, schedule, 0) { - - @Override - public Object operation() throws Exception { - throw new NamingException(); - } - }.perform(); - } catch (NamingException ex) { - // no-op - } - assertThat(result).isEqualTo("Hi!"); - } -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
