LDAP-316 - Optional support for apache commons-pool2 added.

This commit is contained in:
Anindya Chatterjee
2015-10-10 17:03:59 +05:30
committed by Rob Winch
parent 05b89c3b45
commit 4cefc5b147
36 changed files with 4742 additions and 33 deletions

View File

@@ -19,7 +19,8 @@ dependencies {
"org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-orm:$springVersion",
"com.mysema.querydsl:querydsl-apt:$queryDslVersion",
"commons-pool:commons-pool:$commonsPoolVersion"
"commons-pool:commons-pool:$commonsPoolVersion",
"org.apache.commons:commons-pool2:$commonsPool2Version"
provided "com.sun:ldapbp:1.0"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2005-2013 the original author or authors.
* Copyright 2005-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@ import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.pool.PoolExhaustedAction;
import org.springframework.ldap.pool.factory.PoolingContextSource;
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
import org.springframework.ldap.pool2.factory.PoolConfig;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -72,6 +74,19 @@ public class ContextSourceParser implements BeanDefinitionParser {
private static final String ATT_VALIDATION_QUERY_FILTER = "validation-query-filter";
private static final String ATT_VALIDATION_QUERY_SEARCH_CONTROLS_REF = "validation-query-search-controls-ref";
private static final String ATT_NON_TRANSIENT_EXCEPTIONS = "non-transient-exceptions";
private static final String ATT_MAX_IDLE_PER_KEY = "max-idle-per-key";
private static final String ATT_MIN_IDLE_PER_KEY = "min-idle-per-key";
private static final String ATT_MAX_TOTAL_PER_KEY = "max-total-per-key";
private static final String ATT_EVICTION_POLICY_CLASS = "eviction-policy-class";
private static final String ATT_FAIRNESS = "fairness";
private static final String ATT_JMX_ENABLE = "jmx-enable";
private static final String ATT_JMX_NAME_BASE = "jmx-name-base";
private static final String ATT_JMX_NAME_PREFIX = "jmx-name-prefix";
private static final String ATT_LIFO = "lifo";
private static final String ATT_BLOCK_WHEN_EXHAUSTED = "block-when-exhausted";
private static final String ATT_TEST_ON_CREATE = "test-on-create";
private static final String ATT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = "soft-min-evictable-idle-time-millis";
private static final String ATT_USERNAME = "username";
static final String DEFAULT_ID = "contextSource";
@@ -83,6 +98,19 @@ public class ContextSourceParser implements BeanDefinitionParser {
private static final int DEFAULT_EVICTION_RUN_MILLIS = -1;
private static final int DEFAULT_TESTS_PER_EVICTION_RUN = 3;
private static final int DEFAULT_EVICTABLE_MILLIS = 1000 * 60 * 30;
private static final int DEFAULT_MAX_TOTAL_PER_KEY = 8;
private static final int DEFAULT_MAX_IDLE_PER_KEY = 8;
private static final int DEFAULT_MIN_IDLE_PER_KEY = 0;
private static final String DEFAULT_EVICTION_POLICY_CLASS_NAME =
"org.apache.commons.pool2.impl.DefaultEvictionPolicy";
private static final boolean DEFAULT_FAIRNESS = false;
private static final boolean DEFAULT_JMX_ENABLE = true;
private static final String DEFAULT_JMX_NAME_BASE = null;
private static final String DEFAULT_JMX_NAME_PREFIX = "ldap-pool";
private static final boolean DEFAULT_LIFO = true;
private static final int DEFAULT_MAX_WAIT_MILLIS = -1;
private static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true;
private static final int DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1;
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
@@ -150,7 +178,12 @@ public class ContextSourceParser implements BeanDefinitionParser {
boolean nativePooling) {
Element poolingElement = DomUtils.getChildElementByTagName(element, Elements.POOLING);
if(poolingElement == null) {
Element pooling2Element = DomUtils.getChildElementByTagName(element, Elements.POOLING2);
if (pooling2Element != null && poolingElement != null) {
throw new IllegalArgumentException(
String.format("%s cannot be enabled together with %s.", Elements.POOLING2, Elements.POOLING));
} else if (poolingElement == null && pooling2Element == null) {
return targetContextSourceDefinition;
}
@@ -159,26 +192,44 @@ public class ContextSourceParser implements BeanDefinitionParser {
String.format("%s cannot be enabled together with %s", ATT_NATIVE_POOLING, Elements.POOLING));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PoolingContextSource.class);
builder.addPropertyValue("contextSource", targetContextSourceDefinition);
if (pooling2Element != null) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PooledContextSource.class);
builder.addPropertyValue("contextSource", targetContextSourceDefinition);
builder.addPropertyValue("maxActive", getInt(poolingElement, ATT_MAX_ACTIVE, DEFAULT_MAX_ACTIVE));
builder.addPropertyValue("maxTotal", getInt(poolingElement, ATT_MAX_TOTAL, DEFAULT_MAX_TOTAL));
builder.addPropertyValue("maxIdle", getInt(poolingElement, ATT_MAX_IDLE, DEFAULT_MAX_IDLE));
builder.addPropertyValue("minIdle", getInt(poolingElement, ATT_MIN_IDLE, DEFAULT_MIN_IDLE));
builder.addPropertyValue("maxWait", getInt(poolingElement, ATT_MAX_WAIT, DEFAULT_MAX_WAIT));
String whenExhausted = getString(poolingElement, ATT_WHEN_EXHAUSTED, PoolExhaustedAction.BLOCK.name());
builder.addPropertyValue("whenExhaustedAction", PoolExhaustedAction.valueOf(whenExhausted).getValue());
populatePoolConfigProperties(builder, pooling2Element);
boolean testOnBorrow = getBoolean(poolingElement, ATT_TEST_ON_BORROW, false);
boolean testOnReturn = getBoolean(poolingElement, ATT_TEST_ON_RETURN, false);
boolean testWhileIdle = getBoolean(poolingElement, ATT_TEST_WHILE_IDLE, false);
boolean testOnBorrow = getBoolean(pooling2Element, ATT_TEST_ON_BORROW, false);
boolean testOnReturn = getBoolean(pooling2Element, ATT_TEST_ON_RETURN, false);
boolean testWhileIdle = getBoolean(pooling2Element, ATT_TEST_WHILE_IDLE, false);
boolean testOnCreate = getBoolean(pooling2Element, ATT_TEST_ON_CREATE, false);
if(testOnBorrow || testOnReturn || testWhileIdle) {
populatePoolValidationProperties(builder, poolingElement, testOnBorrow, testOnReturn, testWhileIdle);
if (testOnBorrow || testOnCreate || testWhileIdle || testOnReturn) {
populatePoolValidationProperties(builder, pooling2Element);
}
return builder.getBeanDefinition();
} else {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PoolingContextSource.class);
builder.addPropertyValue("contextSource", targetContextSourceDefinition);
builder.addPropertyValue("maxActive", getInt(poolingElement, ATT_MAX_ACTIVE, DEFAULT_MAX_ACTIVE));
builder.addPropertyValue("maxTotal", getInt(poolingElement, ATT_MAX_TOTAL, DEFAULT_MAX_TOTAL));
builder.addPropertyValue("maxIdle", getInt(poolingElement, ATT_MAX_IDLE, DEFAULT_MAX_IDLE));
builder.addPropertyValue("minIdle", getInt(poolingElement, ATT_MIN_IDLE, DEFAULT_MIN_IDLE));
builder.addPropertyValue("maxWait", getInt(poolingElement, ATT_MAX_WAIT, DEFAULT_MAX_WAIT));
String whenExhausted = getString(poolingElement, ATT_WHEN_EXHAUSTED, PoolExhaustedAction.BLOCK.name());
builder.addPropertyValue("whenExhaustedAction", PoolExhaustedAction.valueOf(whenExhausted).getValue());
boolean testOnBorrow = getBoolean(poolingElement, ATT_TEST_ON_BORROW, false);
boolean testOnReturn = getBoolean(poolingElement, ATT_TEST_ON_RETURN, false);
boolean testWhileIdle = getBoolean(poolingElement, ATT_TEST_WHILE_IDLE, false);
if (testOnBorrow || testOnReturn || testWhileIdle) {
populatePoolValidationProperties(builder, poolingElement, testOnBorrow, testOnReturn, testWhileIdle);
}
return builder.getBeanDefinition();
}
return builder.getBeanDefinition();
}
private void populatePoolValidationProperties(BeanDefinitionBuilder builder, Element element,
@@ -216,6 +267,63 @@ public class ContextSourceParser implements BeanDefinitionParser {
builder.addPropertyValue("nonTransientExceptions", nonTransientExceptionClasses);
}
private void populatePoolValidationProperties(BeanDefinitionBuilder builder, Element element) {
BeanDefinitionBuilder validatorBuilder = BeanDefinitionBuilder.rootBeanDefinition(
org.springframework.ldap.pool2.validation.DefaultDirContextValidator.class);
validatorBuilder.addPropertyValue("base", getString(element, ATT_VALIDATION_QUERY_BASE, ""));
validatorBuilder.addPropertyValue("filter",
getString(element, ATT_VALIDATION_QUERY_FILTER,
org.springframework.ldap.pool2.validation.DefaultDirContextValidator.DEFAULT_FILTER));
String searchControlsRef = element.getAttribute(ATT_VALIDATION_QUERY_SEARCH_CONTROLS_REF);
if(StringUtils.hasText(searchControlsRef)) {
validatorBuilder.addPropertyReference("searchControls", searchControlsRef);
}
builder.addPropertyValue("dirContextValidator", validatorBuilder.getBeanDefinition());
String nonTransientExceptions = getString(element, ATT_NON_TRANSIENT_EXCEPTIONS, CommunicationException.class.getName());
String[] strings = StringUtils.commaDelimitedListToStringArray(nonTransientExceptions);
Set<Class<?>> nonTransientExceptionClasses = new HashSet<Class<?>>();
for (String className : strings) {
try {
nonTransientExceptionClasses.add(ClassUtils.getDefaultClassLoader().loadClass(className));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(String.format("%s is not a valid class name", className), e);
}
}
builder.addPropertyValue("nonTransientExceptions", nonTransientExceptionClasses);
}
private void populatePoolConfigProperties(BeanDefinitionBuilder builder, Element element) {
BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder
.rootBeanDefinition(PoolConfig.class);
configBuilder.addPropertyValue("maxTotal", getInt(element, ATT_MAX_TOTAL, DEFAULT_MAX_TOTAL));
configBuilder.addPropertyValue("maxTotalPerKey", getInt(element, ATT_MAX_TOTAL_PER_KEY, DEFAULT_MAX_TOTAL_PER_KEY));
configBuilder.addPropertyValue("maxIdlePerKey", getInt(element, ATT_MAX_IDLE_PER_KEY, DEFAULT_MAX_IDLE_PER_KEY));
configBuilder.addPropertyValue("minIdlePerKey", getInt(element, ATT_MIN_IDLE_PER_KEY, DEFAULT_MIN_IDLE_PER_KEY));
configBuilder.addPropertyValue("evictionPolicyClassName", getString(element, ATT_EVICTION_POLICY_CLASS, DEFAULT_EVICTION_POLICY_CLASS_NAME));
configBuilder.addPropertyValue("fairness", getBoolean(element, ATT_FAIRNESS, DEFAULT_FAIRNESS));
configBuilder.addPropertyValue("jmxEnabled", getBoolean(element, ATT_JMX_ENABLE, DEFAULT_JMX_ENABLE));
configBuilder.addPropertyValue("jmxNameBase", getString(element, ATT_JMX_NAME_BASE, DEFAULT_JMX_NAME_BASE));
configBuilder.addPropertyValue("jmxNamePrefix", getString(element, ATT_JMX_NAME_PREFIX, DEFAULT_JMX_NAME_PREFIX));
configBuilder.addPropertyValue("lifo", getBoolean(element, ATT_LIFO, DEFAULT_LIFO));
configBuilder.addPropertyValue("maxWaitMillis", getInt(element, ATT_MAX_WAIT, DEFAULT_MAX_WAIT_MILLIS));
configBuilder.addPropertyValue("blockWhenExhausted", getBoolean(element, ATT_BLOCK_WHEN_EXHAUSTED, DEFAULT_BLOCK_WHEN_EXHAUSTED));
configBuilder.addPropertyValue("testOnBorrow", getBoolean(element, ATT_TEST_ON_BORROW, false));
configBuilder.addPropertyValue("testOnCreate", getBoolean(element, ATT_TEST_ON_CREATE, false));
configBuilder.addPropertyValue("testOnReturn", getBoolean(element, ATT_TEST_ON_RETURN, false));
configBuilder.addPropertyValue("testWhileIdle", getBoolean(element, ATT_TEST_WHILE_IDLE, false));
configBuilder.addPropertyValue("timeBetweenEvictionRunsMillis", getInt(element, ATT_EVICTION_RUN_MILLIS, DEFAULT_EVICTION_RUN_MILLIS));
configBuilder.addPropertyValue("numTestsPerEvictionRun", getInt(element, ATT_TESTS_PER_EVICTION_RUN, DEFAULT_TESTS_PER_EVICTION_RUN));
configBuilder.addPropertyValue("minEvictableIdleTimeMillis", getInt(element, ATT_EVICTABLE_TIME_MILLIS, DEFAULT_EVICTABLE_MILLIS));
configBuilder.addPropertyValue("softMinEvictableIdleTimeMillis", getInt(element, ATT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS, DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS));
builder.addConstructorArgValue(configBuilder.getBeanDefinition());
}
static class UrlsFactory {
public static String[] urls(String value) {
return StringUtils.commaDelimitedListToStringArray(value);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2005-2013 the original author or authors.
* Copyright 2005-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,12 @@ package org.springframework.ldap.config;
/**
* @author Mattias Hellborg Arthursson
* @author Anindya Chatterjee
*/
public abstract class Elements {
public static final String CONTEXT_SOURCE = "context-source";
public static final String POOLING = "pooling";
public static final String POOLING2 = "pooling2";
public static final String LDAP_TEMPLATE = "ldap-template";
public static final String TRANSACTION_MANAGER = "transaction-manager";
public static final String REPOSITORIES = "repositories";

View File

@@ -0,0 +1,390 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.util.Assert;
import javax.naming.*;
import java.util.Hashtable;
/**
* Used by {@link PooledContextSource} to wrap a {@link Context}, delegating most methods
* to the underlying context, retains a reference to the pool the context was checked out
* from and returns itself to the pool when {@link #close()} is called.
*
* @since 2.0
* @author Eric Dalquist
*/
public class DelegatingContext implements Context {
private KeyedObjectPool keyedObjectPool;
private Context delegateContext;
private final DirContextType dirContextType;
/**
* Create a new delegating context for the specified pool, context and context type.
*
* @param keyedObjectPool The pool the delegate context was checked out from.
* @param delegateContext The context to delegate operations to.
* @param dirContextType The type of context, used as a key for the pool.
* @throws IllegalArgumentException if any of the arguments are null
*/
public DelegatingContext(KeyedObjectPool keyedObjectPool, Context delegateContext, DirContextType dirContextType) {
Assert.notNull(keyedObjectPool, "keyedObjectPool may not be null");
Assert.notNull(delegateContext, "delegateContext may not be null");
Assert.notNull(dirContextType, "dirContextType may not be null");
this.keyedObjectPool = keyedObjectPool;
this.delegateContext = delegateContext;
this.dirContextType = dirContextType;
}
//***** Helper Methods *****//
/**
* @return The direct delegate for this context proxy
*/
public Context getDelegateContext() {
return this.delegateContext;
}
/**
* Recursivley inspect delegates until a non-delegating context is found.
*
* @return The innermost (real) Context that is being delegated to.
*/
public Context getInnermostDelegateContext() {
final Context delegateContext = this.getDelegateContext();
if (delegateContext instanceof DelegatingContext) {
return ((DelegatingContext)delegateContext).getInnermostDelegateContext();
}
return delegateContext;
}
/**
* @throws NamingException If the delegate is null, {@link #close()} has been called.
*/
protected void assertOpen() throws NamingException {
if (this.delegateContext == null) {
throw new NamingException("Context is closed.");
}
}
//***** Object methods *****//
/**
* @see Object#equals(Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Context)) {
return false;
}
final Context thisContext = this.getInnermostDelegateContext();
Context otherContext = (Context)obj;
if (otherContext instanceof DelegatingContext) {
otherContext = ((DelegatingContext)otherContext).getInnermostDelegateContext();
}
return thisContext == otherContext || (thisContext != null && thisContext.equals(otherContext));
}
/**
* @see Object#hashCode()
*/
public int hashCode() {
final Context context = this.getInnermostDelegateContext();
return (context != null ? context.hashCode() : 0);
}
/**
* @see Object#toString()
*/
public String toString() {
final Context context = this.getInnermostDelegateContext();
return (context != null ? context.toString() : "Context is closed");
}
//***** Context Interface Delegates *****//
/**
* @see Context#addToEnvironment(String, Object)
*/
public Object addToEnvironment(String propName, Object propVal) throws NamingException {
throw new UnsupportedOperationException("Cannot call addToEnvironment on a pooled context");
}
/**
* @see Context#bind(Name, Object)
*/
public void bind(Name name, Object obj) throws NamingException {
this.assertOpen();
this.getDelegateContext().bind(name, obj);
}
/**
* @see Context#bind(String, Object)
*/
public void bind(String name, Object obj) throws NamingException {
this.assertOpen();
this.getDelegateContext().bind(name, obj);
}
/**
* @see Context#close()
*/
public void close() throws NamingException {
final Context context = this.getInnermostDelegateContext();
if (context == null) {
return;
}
//Get a local reference so the member can be nulled earlier
this.delegateContext = null;
//Return the object to the Pool and then null the pool reference
try {
boolean valid = true;
if (context instanceof FailureAwareContext) {
FailureAwareContext failureAwareContext = (FailureAwareContext) context;
if(failureAwareContext.hasFailed()) {
valid = false;
}
}
if (valid) {
this.keyedObjectPool.returnObject(this.dirContextType, context);
} else {
this.keyedObjectPool.invalidateObject(this.dirContextType, context);
}
}
catch (Exception e) {
final NamingException namingException = new NamingException("Failed to return delegate Context to pool.");
namingException.setRootCause(e);
throw namingException;
}
finally {
this.keyedObjectPool = null;
}
}
/**
* @see Context#composeName(Name, Name)
*/
public Name composeName(Name name, Name prefix) throws NamingException {
this.assertOpen();
return this.getDelegateContext().composeName(name, prefix);
}
/**
* @see Context#composeName(String, String)
*/
public String composeName(String name, String prefix) throws NamingException {
this.assertOpen();
return this.getDelegateContext().composeName(name, prefix);
}
/**
* @see Context#createSubcontext(Name)
*/
public Context createSubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context");
}
/**
* @see Context#createSubcontext(String)
*/
public Context createSubcontext(String name) throws NamingException {
throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context");
}
/**
* @see Context#destroySubcontext(Name)
*/
public void destroySubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException("Cannot call destroySubcontext on a pooled context");
}
/**
* @see Context#destroySubcontext(String)
*/
public void destroySubcontext(String name) throws NamingException {
throw new UnsupportedOperationException("Cannot call destroySubcontext on a pooled context");
}
/**
* @see Context#getEnvironment()
*/
public Hashtable<?, ?> getEnvironment() throws NamingException {
this.assertOpen();
return this.getDelegateContext().getEnvironment();
}
/**
* @see Context#getNameInNamespace()
*/
public String getNameInNamespace() throws NamingException {
this.assertOpen();
return this.getDelegateContext().getNameInNamespace();
}
/**
* @see Context#getNameParser(Name)
*/
public NameParser getNameParser(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().getNameParser(name);
}
/**
* @see Context#getNameParser(String)
*/
public NameParser getNameParser(String name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().getNameParser(name);
}
/**
* @see Context#list(Name)
*/
public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().list(name);
}
/**
* @see Context#list(String)
*/
public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().list(name);
}
/**
* @see Context#listBindings(Name)
*/
public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().listBindings(name);
}
/**
* @see Context#listBindings(String)
*/
public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().listBindings(name);
}
/**
* @see Context#lookup(Name)
*/
public Object lookup(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().lookup(name);
}
/**
* @see Context#lookup(String)
*/
public Object lookup(String name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().lookup(name);
}
/**
* @see Context#lookupLink(Name)
*/
public Object lookupLink(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().lookupLink(name);
}
/**
* @see Context#lookupLink(String)
*/
public Object lookupLink(String name) throws NamingException {
this.assertOpen();
return this.getDelegateContext().lookupLink(name);
}
/**
* @see Context#rebind(Name, Object)
*/
public void rebind(Name name, Object obj) throws NamingException {
this.assertOpen();
this.getDelegateContext().rebind(name, obj);
}
/**
* @see Context#rebind(String, Object)
*/
public void rebind(String name, Object obj) throws NamingException {
this.assertOpen();
this.getDelegateContext().rebind(name, obj);
}
/**
* @see Context#removeFromEnvironment(String)
*/
public Object removeFromEnvironment(String propName) throws NamingException {
throw new UnsupportedOperationException("Cannot call removeFromEnvironment on a pooled context");
}
/**
* @see Context#rename(Name, Name)
*/
public void rename(Name oldName, Name newName) throws NamingException {
this.assertOpen();
this.getDelegateContext().rename(oldName, newName);
}
/**
* @see Context#rename(String, String)
*/
public void rename(String oldName, String newName) throws NamingException {
this.assertOpen();
this.getDelegateContext().rename(oldName, newName);
}
/**
* @see Context#unbind(Name)
*/
public void unbind(Name name) throws NamingException {
this.assertOpen();
this.getDelegateContext().unbind(name);
}
/**
* @see Context#unbind(String)
*/
public void unbind(String name) throws NamingException {
this.assertOpen();
this.getDelegateContext().unbind(name);
}
}

View File

@@ -0,0 +1,361 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.springframework.ldap.core.DirContextProxy;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.util.Assert;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
/**
* Used by {@link PooledContextSource} to wrap a {@link DirContext}, delegating most methods
* to the underlying context. This class extends {@link DelegatingContext} which handles returning
* the context to the pool on a call to {@link #close()}
*
* @since 2.0
* @author Eric Dalquist
* @author Anindya Chatterjee
*/
public class DelegatingDirContext extends DelegatingContext implements DirContext, DirContextProxy {
private DirContext delegateDirContext;
/**
* Create a new delegating dir context for the specified pool, context and context type.
*
* @param keyedObjectPool The pool the delegate context was checked out from.
* @param delegateDirContext The dir context to delegate operations to.
* @param dirContextType The type of context, used as a key for the pool.
* @throws IllegalArgumentException if any of the arguments are null
*/
public DelegatingDirContext(KeyedObjectPool keyedObjectPool,
DirContext delegateDirContext, DirContextType dirContextType) {
super(keyedObjectPool, delegateDirContext, dirContextType);
Assert.notNull(delegateDirContext, "delegateDirContext may not be null");
this.delegateDirContext = delegateDirContext;
}
//***** Helper Methods *****//
/**
* @return The direct delegate for this dir context proxy
*/
public DirContext getDelegateDirContext() {
return this.delegateDirContext;
}
public Context getDelegateContext() {
return this.getDelegateDirContext();
}
/**
* Recursivley inspect delegates until a non-delegating dir context is found.
*
* @return The innermost (real) DirContext that is being delegated to.
*/
public DirContext getInnermostDelegateDirContext() {
final DirContext delegateDirContext = this.getDelegateDirContext();
if (delegateDirContext instanceof DelegatingDirContext) {
return ((DelegatingDirContext)delegateDirContext).getInnermostDelegateDirContext();
}
return delegateDirContext;
}
protected void assertOpen() throws NamingException {
if (this.delegateDirContext == null) {
throw new NamingException("DirContext is closed.");
}
super.assertOpen();
}
//***** Object methods *****//
/**
* @see Object#equals(Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DirContext)) {
return false;
}
final DirContext thisDirContext = this.getInnermostDelegateDirContext();
DirContext otherDirContext = (DirContext)obj;
if (otherDirContext instanceof DelegatingDirContext) {
otherDirContext = ((DelegatingDirContext)otherDirContext).getInnermostDelegateDirContext();
}
return thisDirContext == otherDirContext || (thisDirContext != null && thisDirContext.equals(otherDirContext));
}
/**
* @see Object#hashCode()
*/
public int hashCode() {
final DirContext context = this.getInnermostDelegateDirContext();
return (context != null ? context.hashCode() : 0);
}
/**
* @see Object#toString()
*/
public String toString() {
final DirContext context = this.getInnermostDelegateDirContext();
return (context != null ? context.toString() : "DirContext is closed");
}
//***** DirContextProxy Interface Methods *****//
/* (non-Javadoc)
* @see org.springframework.ldap.core.DirContextProxy#getTargetContext()
*/
public DirContext getTargetContext() {
return this.getInnermostDelegateDirContext();
}
//***** DirContext Interface Delegates *****//
/**
* @see DirContext#bind(Name, Object, Attributes)
*/
public void bind(Name name, Object obj, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().bind(name, obj, attrs);
}
/**
* @see DirContext#bind(String, Object, Attributes)
*/
public void bind(String name, Object obj, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().bind(name, obj, attrs);
}
/**
* @see DirContext#createSubcontext(Name, Attributes)
*/
public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException {
throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context");
}
/**
* @see DirContext#createSubcontext(String, Attributes)
*/
public DirContext createSubcontext(String name, Attributes attrs) throws NamingException {
throw new UnsupportedOperationException("Cannot call createSubcontext on a pooled context");
}
/**
* @see DirContext#getAttributes(Name, String[])
*/
public Attributes getAttributes(Name name, String[] attrIds) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().getAttributes(name, attrIds);
}
/**
* @see DirContext#getAttributes(Name)
*/
public Attributes getAttributes(Name name) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().getAttributes(name);
}
/**
* @see DirContext#getAttributes(String, String[])
*/
public Attributes getAttributes(String name, String[] attrIds) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().getAttributes(name, attrIds);
}
/**
* @see DirContext#getAttributes(String)
*/
public Attributes getAttributes(String name) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().getAttributes(name);
}
/**
* @see DirContext#getSchema(Name)
*/
public DirContext getSchema(Name name) throws NamingException {
throw new UnsupportedOperationException("Cannot call getSchema on a pooled context");
}
/**
* @see DirContext#getSchema(String)
*/
public DirContext getSchema(String name) throws NamingException {
throw new UnsupportedOperationException("Cannot call getSchema on a pooled context");
}
/**
* @see DirContext#getSchemaClassDefinition(Name)
*/
public DirContext getSchemaClassDefinition(Name name) throws NamingException {
throw new UnsupportedOperationException("Cannot call getSchemaClassDefinition on a pooled context");
}
/**
* @see DirContext#getSchemaClassDefinition(String)
*/
public DirContext getSchemaClassDefinition(String name) throws NamingException {
throw new UnsupportedOperationException("Cannot call getSchemaClassDefinition on a pooled context");
}
/**
* @see DirContext#modifyAttributes(Name, int, Attributes)
*/
public void modifyAttributes(Name name, int modOp, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().modifyAttributes(name, modOp, attrs);
}
/**
* @see DirContext#modifyAttributes(Name, ModificationItem[])
*/
public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().modifyAttributes(name, mods);
}
/**
* @see DirContext#modifyAttributes(String, int, Attributes)
*/
public void modifyAttributes(String name, int modOp, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().modifyAttributes(name, modOp, attrs);
}
/**
* @see DirContext#modifyAttributes(String, ModificationItem[])
*/
public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().modifyAttributes(name, mods);
}
/**
* @see DirContext#rebind(Name, Object, Attributes)
*/
public void rebind(Name name, Object obj, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().rebind(name, obj, attrs);
}
/**
* @see DirContext#rebind(String, Object, Attributes)
*/
public void rebind(String name, Object obj, Attributes attrs) throws NamingException {
this.assertOpen();
this.getDelegateDirContext().rebind(name, obj, attrs);
}
/**
* @see DirContext#search(Name, Attributes, String[])
*/
public NamingEnumeration<SearchResult> search(Name name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, matchingAttributes, attributesToReturn);
}
/**
* @see DirContext#search(Name, Attributes)
*/
public NamingEnumeration<SearchResult> search(Name name, Attributes matchingAttributes) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, matchingAttributes);
}
/**
* @see DirContext#search(Name, String, Object[], SearchControls)
*/
public NamingEnumeration<SearchResult> search(Name name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, filterExpr, filterArgs, cons);
}
/**
* @see DirContext#search(Name, String, SearchControls)
*/
public NamingEnumeration<SearchResult> search(Name name, String filter, SearchControls cons) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, filter, cons);
}
/**
* @see DirContext#search(String, Attributes, String[])
*/
public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, matchingAttributes, attributesToReturn);
}
/**
* @see DirContext#search(String, Attributes)
*/
public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, matchingAttributes);
}
/**
* @see DirContext#search(String, String, Object[], SearchControls)
*/
public NamingEnumeration<SearchResult> search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, filterExpr, filterArgs, cons);
}
/**
* @see DirContext#search(String, String, SearchControls)
*/
public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) throws NamingException {
this.assertOpen();
return this.getDelegateDirContext().search(name, filter, cons);
}
/**
* @see DelegatingContext#close()
*/
public void close() throws NamingException {
if (this.delegateDirContext == null) {
return;
}
super.close();
this.delegateDirContext = null;
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.util.Assert;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.ExtendedRequest;
import javax.naming.ldap.ExtendedResponse;
import javax.naming.ldap.LdapContext;
/**
* Used by {@link PooledContextSource} to wrap a {@link LdapContext}, delegating most methods
* to the underlying context. This class extends {@link DelegatingDirContext} which handles returning
* the context to the pool on a call to {@link #close()}
*
* @since 2.0
* @author Eric Dalquist
* @author Anindya Chatterjee
*/
public class DelegatingLdapContext extends DelegatingDirContext implements LdapContext {
private LdapContext delegateLdapContext;
/**
* Create a new delegating ldap context for the specified pool, context and context type.
*
* @param keyedObjectPool The pool the delegate context was checked out from.
* @param delegateLdapContext The ldap context to delegate operations to.
* @param dirContextType The type of context, used as a key for the pool.
* @throws IllegalArgumentException if any of the arguments are null
*/
public DelegatingLdapContext(KeyedObjectPool keyedObjectPool,
LdapContext delegateLdapContext, DirContextType dirContextType) {
super(keyedObjectPool, delegateLdapContext, dirContextType);
Assert.notNull(delegateLdapContext, "delegateLdapContext may not be null");
this.delegateLdapContext = delegateLdapContext;
}
//***** Helper Methods *****//
/**
* @return The direct delegate for this ldap context proxy
*/
public LdapContext getDelegateLdapContext() {
return this.delegateLdapContext;
}
// cannot return subtype in overridden method unless Java5
public DirContext getDelegateDirContext() {
return this.getDelegateLdapContext();
}
/**
* Recursivley inspect delegates until a non-delegating ldap context is found.
*
* @return The innermost (real) DirContext that is being delegated to.
*/
public LdapContext getInnermostDelegateLdapContext() {
final LdapContext delegateLdapContext = this.getDelegateLdapContext();
if (delegateLdapContext instanceof DelegatingLdapContext) {
return ((DelegatingLdapContext)delegateLdapContext).getInnermostDelegateLdapContext();
}
return delegateLdapContext;
}
protected void assertOpen() throws NamingException {
if (this.delegateLdapContext == null) {
throw new NamingException("LdapContext is closed.");
}
super.assertOpen();
}
//***** Object methods *****//
/**
* @see Object#equals(Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof LdapContext)) {
return false;
}
final LdapContext thisLdapContext = this.getInnermostDelegateLdapContext();
LdapContext otherLdapContext = (LdapContext)obj;
if (otherLdapContext instanceof DelegatingLdapContext) {
otherLdapContext = ((DelegatingLdapContext)otherLdapContext).getInnermostDelegateLdapContext();
}
return thisLdapContext == otherLdapContext || (thisLdapContext != null && thisLdapContext.equals(otherLdapContext));
}
/**
* @see Object#hashCode()
*/
public int hashCode() {
final LdapContext context = this.getInnermostDelegateLdapContext();
return (context != null ? context.hashCode() : 0);
}
/**
* @see Object#toString()
*/
public String toString() {
final LdapContext context = this.getInnermostDelegateLdapContext();
return (context != null ? context.toString() : "LdapContext is closed");
}
//***** LdapContext Interface Delegates *****//
/**
* @see LdapContext#extendedOperation(ExtendedRequest)
*/
public ExtendedResponse extendedOperation(ExtendedRequest request) throws NamingException {
this.assertOpen();
return this.getDelegateLdapContext().extendedOperation(request);
}
/**
* @see LdapContext#getConnectControls()
*/
public Control[] getConnectControls() throws NamingException {
this.assertOpen();
return this.getDelegateLdapContext().getConnectControls();
}
/**
* @see LdapContext#getRequestControls()
*/
public Control[] getRequestControls() throws NamingException {
this.assertOpen();
return this.getDelegateLdapContext().getRequestControls();
}
/**
* @see LdapContext#getResponseControls()
*/
public Control[] getResponseControls() throws NamingException {
this.assertOpen();
return this.getDelegateLdapContext().getResponseControls();
}
/**
* @see LdapContext#newInstance(Control[])
*/
public LdapContext newInstance(Control[] requestControls) throws NamingException {
throw new UnsupportedOperationException("Cannot call newInstance on a pooled context");
}
/**
* @see LdapContext#reconnect(Control[])
*/
public void reconnect(Control[] connCtls) throws NamingException {
throw new UnsupportedOperationException("Cannot call reconnect on a pooled context");
}
/**
* @see LdapContext#setRequestControls(Control[])
*/
public void setRequestControls(Control[] requestControls) throws NamingException {
throw new UnsupportedOperationException("Cannot call setRequestControls on a pooled context");
}
/**
* @see DelegatingDirContext#close()
*/
public void close() throws NamingException {
if (this.delegateLdapContext == null) {
return;
}
super.close();
this.delegateLdapContext = null;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.springframework.ldap.core.ContextSource;
import javax.naming.directory.DirContext;
/**
* An enum representing the two types of {@link DirContext}s that can be returned by a
* {@link ContextSource}.
*
* @author Eric Dalquist
*/
public final class DirContextType {
private String name;
private DirContextType(String name) {
this.name = name;
}
public String toString() {
return name;
}
/**
* The type of {@link DirContext} returned by {@link ContextSource#getReadOnlyContext()}
*/
public static final DirContextType READ_ONLY = new DirContextType("READ_ONLY");
/**
* The type of {@link DirContext} returned by {@link ContextSource#getReadWriteContext()}
*/
public static final DirContextType READ_WRITE = new DirContextType("READ_WRITE");
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
/**
* @author Mattias Hellborg Arthursson
*/
public interface FailureAwareContext {
boolean hasFailed();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.springframework.ldap.pool2.factory.MutablePooledContextSource;
import javax.naming.NamingException;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
/**
* Used by {@link MutablePooledContextSource} to wrap a {@link LdapContext},
* delegating most methods to the underlying context. This class extends
* {@link DelegatingLdapContext}, allowing request controls to be set on the
* wrapped ldap context. This enables the Spring LDAP pooling to be used for
* scenarios such as paged results.
*
* @since 2.0
* @author Ulrik Sandberg
* @author Anindya Chatterjee
*/
public class MutableDelegatingLdapContext extends DelegatingLdapContext {
/**
* Create a new mutable delegating ldap context for the specified pool,
* context and context type.
*
* @param keyedObjectPool The pool the delegate context was checked out
* from.
* @param delegateLdapContext The ldap context to delegate operations to.
* @param dirContextType The type of context, used as a key for the pool.
* @throws IllegalArgumentException if any of the arguments are null
*/
public MutableDelegatingLdapContext(KeyedObjectPool keyedObjectPool, LdapContext delegateLdapContext,
DirContextType dirContextType) {
super(keyedObjectPool, delegateLdapContext, dirContextType);
}
public void setRequestControls(Control[] requestControls) throws NamingException {
assertOpen();
getDelegateLdapContext().setRequestControls(requestControls);
}
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextProxy;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.FailureAwareContext;
import org.springframework.ldap.pool2.validation.DirContextValidator;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.util.Assert;
import javax.naming.CommunicationException;
import javax.naming.directory.DirContext;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Factory that creates {@link DirContext} instances for pooling via a
* configured {@link ContextSource}. The {@link DirContext}s are keyed based
* on if they are read only or read/write. The expected key type is the
* {@link org.springframework.ldap.pool2.DirContextType} enum.
*
* <br>
* <br>
* Configuration: <table border="1">
* <tr>
* <th align="left">Property</th>
* <th align="left">Description</th>
* <th align="left">Required</th>
* <th align="left">Default</th>
* </tr>
* <tr>
* <td valign="top">contextSource</td>
* <td valign="top"> The {@link ContextSource} to get {@link DirContext}s from
* for adding to the pool. </td>
* <td valign="top">Yes</td>
* <td valign="top">null</td>
* </tr>
* <tr>
* <td valign="top">dirContextValidator</td>
* <td valign="top"> The {@link DirContextValidator} to use to validate
* {@link DirContext}s. This is only required if the pool has validation of any
* kind turned on. </td>
* <td valign="top">No</td>
* <td valign="top">null</td>
* </tr>
* </table>
*
* @since 2.0
* @author Eric Dalquist <a
* href="mailto:eric.dalquist@doit.wisc.edu">eric.dalquist@doit.wisc.edu</a>
* @author Mattias Hellborg Arthursson
* @author Anindya Chatterjee
*/
class DirContextPooledObjectFactory extends BaseKeyedPooledObjectFactory {
/**
* Logger for this class and subclasses
*/
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Set<Class<? extends Throwable>> DEFAULT_NONTRANSIENT_EXCEPTIONS
= new HashSet<Class<? extends Throwable>>(){{
add(CommunicationException.class);
}};
private ContextSource contextSource;
private DirContextValidator dirContextValidator;
private Set<Class<? extends Throwable>> nonTransientExceptions = DEFAULT_NONTRANSIENT_EXCEPTIONS;
void setNonTransientExceptions(Collection<Class<? extends Throwable>> nonTransientExceptions) {
this.nonTransientExceptions = new HashSet<Class<? extends Throwable>>(nonTransientExceptions);
}
/**
* @return the contextSource
*/
public ContextSource getContextSource() {
return this.contextSource;
}
/**
* @param contextSource
* the contextSource to set
*/
public void setContextSource(ContextSource contextSource) {
if (contextSource == null) {
throw new IllegalArgumentException("contextSource may not be null");
}
this.contextSource = contextSource;
}
/**
* @return the dirContextValidator
*/
public DirContextValidator getDirContextValidator() {
return this.dirContextValidator;
}
/**
* @param dirContextValidator
* the dirContextValidator to set
*/
public void setDirContextValidator(DirContextValidator dirContextValidator) {
if (dirContextValidator == null) {
throw new IllegalArgumentException(
"dirContextValidator may not be null");
}
this.dirContextValidator = dirContextValidator;
}
private Object makeFailureAwareProxy(DirContext readOnlyContext) {
return Proxy.newProxyInstance(DirContextProxy.class
.getClassLoader(),
new Class<?>[]{
LdapUtils.getActualTargetClass(readOnlyContext),
DirContextProxy.class,
FailureAwareContext.class},
new FailureAwareContextProxy(readOnlyContext));
}
/**
* @see BaseKeyedPooledObjectFactory#validateObject(Object, PooledObject)
*
* */
@Override
public boolean validateObject(Object key, PooledObject pooledObject) {
Assert.notNull(this.dirContextValidator,
"DirContextValidator may not be null");
Assert.isTrue(key instanceof DirContextType,
"key must be a DirContextType");
Assert.notNull(pooledObject,
"The Object to validate must not be null");
Assert.isTrue(pooledObject.getObject() instanceof DirContext,
"The Object to validate must be of type '" + DirContext.class
+ "'");
try {
final DirContextType contextType = (DirContextType) key;
final DirContext dirContext = (DirContext) pooledObject.getObject();
return this.dirContextValidator.validateDirContext(contextType,
dirContext);
} catch (Exception e) {
this.logger.warn("Failed to validate '" + pooledObject.getObject()
+ "' due to an unexpected exception.", e);
return false;
}
}
/**
* @see BaseKeyedPooledObjectFactory#destroyObject(Object, PooledObject)
*
* */
@Override
public void destroyObject(Object key, PooledObject pooledObject) throws Exception {
Assert.notNull(pooledObject,
"The Object to destroy must not be null");
Assert.isTrue(pooledObject.getObject() instanceof DirContext,
"The Object to destroy must be of type '" + DirContext.class
+ "'");
try {
final DirContext dirContext = (DirContext) pooledObject.getObject();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Closing " + key + " DirContext='"
+ dirContext + "'");
}
dirContext.close();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Closed " + key + " DirContext='"
+ dirContext + "'");
}
} catch (Exception e) {
this.logger.warn(
"An exception occured while closing '" + pooledObject.getObject() + "'", e);
}
}
/**
* @see BaseKeyedPooledObjectFactory#create(Object)
*
* */
@Override
public Object create(Object key) throws Exception {
Assert.notNull(this.contextSource, "ContextSource may not be null");
Assert.isTrue(key instanceof DirContextType,
"key must be a DirContextType");
final DirContextType contextType = (DirContextType) key;
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating a new " + contextType + " DirContext");
}
if (contextType == DirContextType.READ_WRITE) {
final DirContext readWriteContext = this.contextSource
.getReadWriteContext();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Created new " + DirContextType.READ_WRITE
+ " DirContext='" + readWriteContext + "'");
}
return makeFailureAwareProxy(readWriteContext);
} else if (contextType == DirContextType.READ_ONLY) {
final DirContext readOnlyContext = this.contextSource
.getReadOnlyContext();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Created new " + DirContextType.READ_ONLY
+ " DirContext='" + readOnlyContext + "'");
}
return makeFailureAwareProxy(readOnlyContext);
} else {
throw new IllegalArgumentException("Unrecognized ContextType: "
+ contextType);
}
}
/**
* @see BaseKeyedPooledObjectFactory#wrap(Object)
*
* */
@Override
public PooledObject wrap(Object value) {
return new DefaultPooledObject(value);
}
/**
* Invocation handler that checks thrown exceptions against the configured {@link #nonTransientExceptions},
* marking the Context as invalid on match.
*
* @author Mattias Hellborg Arthursson
* @since 2.0
*/
private class FailureAwareContextProxy implements
InvocationHandler {
private DirContext target;
private boolean hasFailed = false;
public FailureAwareContextProxy(DirContext target) {
Assert.notNull(target, "Target must not be null");
this.target = target;
}
/*
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if (methodName.equals("getTargetContext")) {
return target;
} else if (methodName.equals("hasFailed")) {
return hasFailed;
}
try {
return method.invoke(target, args);
}
catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
Class<? extends Throwable> targetExceptionClass = targetException.getClass();
boolean nonTransientEncountered = false;
for (Class<? extends Throwable> clazz : nonTransientExceptions) {
if(clazz.isAssignableFrom(targetExceptionClass)) {
logger.info(
String.format("An %s - explicitly configured to be a non-transient exception - encountered; eagerly invalidating the target context.",
targetExceptionClass));
nonTransientEncountered = true;
break;
}
}
if(nonTransientEncountered) {
hasFailed = true;
} else {
if (logger.isDebugEnabled()) {
logger.debug(String.format("An %s - not explicitly configured to be a non-transient exception - encountered; ignoring.",
targetExceptionClass));
}
}
throw targetException;
}
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.DelegatingDirContext;
import org.springframework.ldap.pool2.MutableDelegatingLdapContext;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
/**
* A {@link PooledContextSource} subclass that creates
* {@link MutableDelegatingLdapContext} instances. This enables the Spring LDAP
* pooling to be used in scenarios that require request controls to be set, such
* as paged results.
*
* @since 2.0
* @author Anindya Chatterjee
*/
public class MutablePooledContextSource extends PooledContextSource {
/**
* Creates a new pooling context source, setting up the DirContext object
* factory and generic keyed object pool.
*
* @param poolConfig pool configurations to set.
*/
public MutablePooledContextSource(PoolConfig poolConfig) {
super(poolConfig);
}
protected DirContext getContext(DirContextType dirContextType) {
final DirContext dirContext;
try {
dirContext = (DirContext) this.keyedObjectPool.borrowObject(dirContextType);
}
catch (Exception e) {
throw new DataAccessResourceFailureException("Failed to borrow DirContext from pool.", e);
}
if (dirContext instanceof LdapContext) {
return new MutableDelegatingLdapContext(this.keyedObjectPool, (LdapContext) dirContext, dirContextType);
}
return new DelegatingDirContext(this.keyedObjectPool, dirContext, dirContextType);
}
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
/**
* A wrapper class for the pool configuration. It helps to create an instance of
* {@link GenericKeyedObjectPoolConfig}.
*
* @author Anindya Chatterjee
* @since 2.0
*/
public class PoolConfig {
private int maxIdlePerKey = 8;
private int maxTotal = -1;
private int maxTotalPerKey = 8;
private int minIdlePerKey = 0;
private boolean blockWhenExhausted = true;
private String evictionPolicyClassName = "org.apache.commons.pool2.impl.DefaultEvictionPolicy";
private boolean fairness = false;
private boolean jmxEnabled = true;
private String jmxNameBase = null;
private String jmxNamePrefix = "ldap-pool";
private boolean lifo = true;
private long maxWaitMillis = -1L;
private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
private int numTestsPerEvictionRun = 3;
private long softMinEvictableIdleTimeMillis = -1L;
private boolean testOnBorrow = false;
private boolean testOnCreate = false;
private boolean testOnReturn = false;
private boolean testWhileIdle = false;
private long timeBetweenEvictionRunsMillis = -1L;
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMaxIdlePerKey(int)
*
*/
public void setMaxIdlePerKey(int maxIdlePerKey) {
this.maxIdlePerKey = maxIdlePerKey;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMaxTotal(int)
*
*/
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMaxTotalPerKey(int)
*/
public void setMaxTotalPerKey(int maxTotalPerKey) {
this.maxTotalPerKey = maxTotalPerKey;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMinIdlePerKey(int)
*/
public void setMinIdlePerKey(int minIdlePerKey) {
this.minIdlePerKey = minIdlePerKey;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setBlockWhenExhausted(boolean)
*/
public void setBlockWhenExhausted(boolean blockWhenExhausted) {
this.blockWhenExhausted = blockWhenExhausted;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setEvictionPolicyClassName(String)
*/
public void setEvictionPolicyClassName(String evictionPolicyClassName) {
this.evictionPolicyClassName = evictionPolicyClassName;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setFairness(boolean)
*/
public void setFairness(boolean fairness) {
this.fairness = fairness;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setJmxEnabled(boolean)
*/
public void setJmxEnabled(boolean jmxEnabled) {
this.jmxEnabled = jmxEnabled;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setJmxNameBase(String)
*/
public void setJmxNameBase(String jmxNameBase) {
this.jmxNameBase = jmxNameBase;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setJmxNamePrefix(String)
*/
public void setJmxNamePrefix(String jmxNamePrefix) {
this.jmxNamePrefix = jmxNamePrefix;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setLifo(boolean)
*/
public void setLifo(boolean lifo) {
this.lifo = lifo;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMaxWaitMillis(long)
*/
public void setMaxWaitMillis(long maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setMinEvictableIdleTimeMillis(long)
*/
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setNumTestsPerEvictionRun(int)
*/
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
this.numTestsPerEvictionRun = numTestsPerEvictionRun;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setSoftMinEvictableIdleTimeMillis(long)
*/
public void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setTestOnBorrow(boolean)
*/
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setTestOnCreate(boolean)
*/
public void setTestOnCreate(boolean testOnCreate) {
this.testOnCreate = testOnCreate;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setTestOnReturn(boolean)
*/
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setTestWhileIdle(boolean)
*/
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
/**
* @see org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig#setTimeBetweenEvictionRunsMillis(long)
*/
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
/**
* @see GenericKeyedObjectPoolConfig#getMaxIdlePerKey()
*/
public int getMaxIdlePerKey() {
return maxIdlePerKey;
}
/**
* @see GenericKeyedObjectPoolConfig#getMaxTotal()
*/
public int getMaxTotal() {
return maxTotal;
}
/**
* @see GenericKeyedObjectPoolConfig#getMaxIdlePerKey()
*/
public int getMaxTotalPerKey() {
return maxTotalPerKey;
}
/**
* @see GenericKeyedObjectPoolConfig#getMinIdlePerKey()
*/
public int getMinIdlePerKey() {
return minIdlePerKey;
}
/**
* @see GenericKeyedObjectPoolConfig#getBlockWhenExhausted()
*/
public boolean isBlockWhenExhausted() {
return blockWhenExhausted;
}
/**
* @see GenericKeyedObjectPoolConfig#getEvictionPolicyClassName()
*/
public String getEvictionPolicyClassName() {
return evictionPolicyClassName;
}
/**
* @see GenericKeyedObjectPoolConfig#getFairness()
*/
public boolean isFairness() {
return fairness;
}
/**
* @see GenericKeyedObjectPoolConfig#getJmxEnabled()
*/
public boolean isJmxEnabled() {
return jmxEnabled;
}
/**
* @see GenericKeyedObjectPoolConfig#getJmxNameBase()
*/
public String getJmxNameBase() {
return jmxNameBase;
}
/**
* @see GenericKeyedObjectPoolConfig#getJmxNamePrefix()
*/
public String getJmxNamePrefix() {
return jmxNamePrefix;
}
/**
* @see GenericKeyedObjectPoolConfig#getLifo()
*/
public boolean isLifo() {
return lifo;
}
/**
* @see GenericKeyedObjectPoolConfig#getMaxWaitMillis()
*/
public long getMaxWaitMillis() {
return maxWaitMillis;
}
/**
* @see GenericKeyedObjectPoolConfig#getMinEvictableIdleTimeMillis()
*/
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
/**
* @see GenericKeyedObjectPoolConfig#getNumTestsPerEvictionRun()
*/
public int getNumTestsPerEvictionRun() {
return numTestsPerEvictionRun;
}
/**
* @see GenericKeyedObjectPoolConfig#getSoftMinEvictableIdleTimeMillis()
*/
public long getSoftMinEvictableIdleTimeMillis() {
return softMinEvictableIdleTimeMillis;
}
/**
* @see GenericKeyedObjectPoolConfig#getTestOnBorrow()
*/
public boolean isTestOnBorrow() {
return testOnBorrow;
}
/**
* @see GenericKeyedObjectPoolConfig#getTestOnCreate()
*/
public boolean isTestOnCreate() {
return testOnCreate;
}
/**
* @see GenericKeyedObjectPoolConfig#getTestOnReturn()
*/
public boolean isTestOnReturn() {
return testOnReturn;
}
/**
* @see GenericKeyedObjectPoolConfig#getTestWhileIdle()
*/
public boolean isTestWhileIdle() {
return testWhileIdle;
}
/**
* @see GenericKeyedObjectPoolConfig#getTimeBetweenEvictionRunsMillis()
*/
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
}

View File

@@ -0,0 +1,310 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.support.DelegatingBaseLdapPathContextSourceSupport;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.validation.DirContextValidator;
import org.springframework.ldap.pool2.DelegatingDirContext;
import org.springframework.ldap.pool2.DelegatingLdapContext;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
import java.util.Collection;
/**
* A {@link ContextSource} implementation that wraps an object pool and another
* {@link ContextSource}. {@link DirContext}s are retrieved from the pool which
* maintains them.
*
* NOTE: This implementation is based on apache commons-pool2.
* <br>
* <br>
* Configuration:
* <table border="1" summary="Configuration">
* <tr>
* <th align="left">Property</th> <th align="left">Description</th> <th
* align="left">Required</th> <th align="left">Default</th>
* </tr>
* <tr>
* <td valign="top">contextSource</td>
* <td valign="top">
* The {@link ContextSource} to get {@link DirContext}s from for adding to the
* pool.</td>
* <td valign="top">Yes</td>
* <td valign="top">null</td>
* </tr>
* <tr>
* <td valign="top">dirContextValidator</td>
* <td valign="top">
* The {@link org.springframework.ldap.pool2.validation.DirContextValidator} to use for validating {@link DirContext}s.
* Required if any of the test/validate options are enabled.</td>
* <td valign="top">No</td>
* <td valign="top">null</td>
* </tr>
* <tr>
* <td valign="top">poolConfig</td>
* <td valign="top">The {@link PoolConfig} to configure the pool.</td>
* <td valign="top">No</td>
* <td valign="top">null</td>
* </tr>
* </table>
*
* @since 2.0
* @author Eric Dalquist
* @author Anindya Chatterjee
*/
public class PooledContextSource
extends DelegatingBaseLdapPathContextSourceSupport
implements ContextSource, DisposableBean {
/**
* The logger for this class and sub-classes
*/
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected final GenericKeyedObjectPool keyedObjectPool;
private final DirContextPooledObjectFactory dirContextPooledObjectFactory;
private PoolConfig poolConfig;
/**
* Creates a new pooling context source, setting up the DirContext object
* factory and generic keyed object pool.
*/
public PooledContextSource(PoolConfig poolConfig) {
this.dirContextPooledObjectFactory = new DirContextPooledObjectFactory();
if (poolConfig != null) {
this.poolConfig = poolConfig;
GenericKeyedObjectPoolConfig objectPoolConfig = getConfig(poolConfig);
this.keyedObjectPool =
new GenericKeyedObjectPool(this.dirContextPooledObjectFactory, objectPoolConfig);
} else {
this.keyedObjectPool =
new GenericKeyedObjectPool(this.dirContextPooledObjectFactory);
}
}
// ***** Pool Property Configuration *****//
/**
* @return the poolConfig
* */
public PoolConfig getPoolConfig() {
return poolConfig;
}
/**
* @see GenericKeyedObjectPool#getNumIdle()
* */
public int getNumIdle() {
return this.keyedObjectPool.getNumIdle();
}
/**
* @see GenericKeyedObjectPool#getNumIdle(Object)
* */
public int getNumIdleRead() {
return this.keyedObjectPool.getNumIdle(DirContextType.READ_ONLY);
}
/**
* @see GenericKeyedObjectPool#getNumIdle(Object)
* */
public int getNumIdleWrite() {
return this.keyedObjectPool.getNumIdle(DirContextType.READ_WRITE);
}
/**
* @see GenericKeyedObjectPool#getNumActive()
* */
public int getNumActive() {
return this.keyedObjectPool.getNumActive();
}
/**
* @see GenericKeyedObjectPool#getNumActive(Object)
* */
public int getNumActiveRead() {
return this.keyedObjectPool.getNumActive(DirContextType.READ_ONLY);
}
/**
* @see GenericKeyedObjectPool#getNumActive(Object)
* */
public int getNumActiveWrite() {
return this.keyedObjectPool.getNumActive(DirContextType.READ_WRITE);
}
/**
* @see GenericKeyedObjectPool#getNumWaiters()
* */
public int getNumWaiters() {
return this.keyedObjectPool.getNumWaiters();
}
// ***** Object Factory Property Configuration *****//
/**
* @return the contextSource
*/
public ContextSource getContextSource() {
return this.dirContextPooledObjectFactory.getContextSource();
}
/**
* @return the dirContextValidator
*/
public DirContextValidator getDirContextValidator() {
return this.dirContextPooledObjectFactory.getDirContextValidator();
}
/**
* @param contextSource the contextSource to set
* Required
*/
public void setContextSource(ContextSource contextSource) {
this.dirContextPooledObjectFactory.setContextSource(contextSource);
}
/**
* @param dirContextValidator the dirContextValidator to set
* Required
*/
public void setDirContextValidator(DirContextValidator dirContextValidator) {
this.dirContextPooledObjectFactory.setDirContextValidator(dirContextValidator);
}
/**
* Configure the exception classes that are to be interpreted as no-transient with regards to eager
* context invalidation. If one of the configured exceptions (or subclasses of them)
* is thrown by any method on a pooled DirContext, that instance will immediately be marked
* as invalid without any additional testing (i.e. testOnReturn).
* This allows for more efficient management of dead connections.
* Default is {@link javax.naming.CommunicationException}.
*
* @param nonTransientExceptions the exception classes that should be interpreted as non-transient
* with regards to eager invalidation.
* @since 2.0
*/
public void setNonTransientExceptions(Collection<Class<? extends Throwable>> nonTransientExceptions) {
this.dirContextPooledObjectFactory.setNonTransientExceptions(nonTransientExceptions);
}
// ***** DisposableBean interface methods *****//
/*
* (non-Javadoc)
*
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
try {
this.keyedObjectPool.close();
}
catch (Exception e) {
this.logger.warn("An exception occurred while closing the underlying pool.", e);
}
}
@Override
protected ContextSource getTarget() {
return getContextSource();
}
// ***** ContextSource interface methods *****//
@Override
public DirContext getReadOnlyContext() {
return this.getContext(DirContextType.READ_ONLY);
}
@Override
public DirContext getReadWriteContext() {
return this.getContext(DirContextType.READ_WRITE);
}
/**
* Gets a DirContext of the specified type from the keyed object pool.
*
* @param dirContextType The type of context to return.
* @return A wrapped DirContext of the specified type.
* @throws DataAccessResourceFailureException If retrieving the object from
* the pool throws an exception
*/
protected DirContext getContext(DirContextType dirContextType) {
final DirContext dirContext;
try {
dirContext = (DirContext) this.keyedObjectPool.borrowObject(dirContextType);
}
catch (Exception e) {
throw new DataAccessResourceFailureException("Failed to borrow DirContext from pool.", e);
}
if (dirContext instanceof LdapContext) {
return new DelegatingLdapContext(this.keyedObjectPool, (LdapContext) dirContext, dirContextType);
}
return new DelegatingDirContext(this.keyedObjectPool, dirContext, dirContextType);
}
@Override
public DirContext getContext(String principal, String credentials) {
throw new UnsupportedOperationException("Not supported for this implementation");
}
private GenericKeyedObjectPoolConfig getConfig(PoolConfig poolConfig) {
GenericKeyedObjectPoolConfig objectPoolConfig = new GenericKeyedObjectPoolConfig();
objectPoolConfig.setMaxTotalPerKey(poolConfig.getMaxTotalPerKey());
objectPoolConfig.setMaxTotal(poolConfig.getMaxTotal());
objectPoolConfig.setMaxIdlePerKey(poolConfig.getMaxIdlePerKey());
objectPoolConfig.setMinIdlePerKey(poolConfig.getMinIdlePerKey());
objectPoolConfig.setTestWhileIdle(poolConfig.isTestWhileIdle());
objectPoolConfig.setTestOnReturn(poolConfig.isTestOnReturn());
objectPoolConfig.setTestOnCreate(poolConfig.isTestOnCreate());
objectPoolConfig.setTestOnBorrow(poolConfig.isTestOnBorrow());
objectPoolConfig.setTimeBetweenEvictionRunsMillis(poolConfig.getTimeBetweenEvictionRunsMillis());
objectPoolConfig.setEvictionPolicyClassName(poolConfig.getEvictionPolicyClassName());
objectPoolConfig.setMinEvictableIdleTimeMillis(poolConfig.getMinEvictableIdleTimeMillis());
objectPoolConfig.setNumTestsPerEvictionRun(poolConfig.getNumTestsPerEvictionRun());
objectPoolConfig.setSoftMinEvictableIdleTimeMillis(poolConfig.getSoftMinEvictableIdleTimeMillis());
objectPoolConfig.setJmxEnabled(poolConfig.isJmxEnabled());
objectPoolConfig.setJmxNameBase(poolConfig.getJmxNameBase());
objectPoolConfig.setJmxNamePrefix(poolConfig.getJmxNamePrefix());
objectPoolConfig.setMaxWaitMillis(poolConfig.getMaxWaitMillis());
objectPoolConfig.setFairness(poolConfig.isFairness());
objectPoolConfig.setBlockWhenExhausted(poolConfig.isBlockWhenExhausted());
objectPoolConfig.setLifo(poolConfig.isLifo());
return objectPoolConfig;
}
}

View File

@@ -0,0 +1,7 @@
<html>
<body>
Core classes for the pooling library based on commons-pool2 library.
</body>
</html>

View File

@@ -0,0 +1,7 @@
<html>
<body>
Base classes for the pooling library based on commons-pool2 library.
</body>
</html>

View File

@@ -0,0 +1,191 @@
/*
* Copyright 2005-2013 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.validation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.util.Assert;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/**
* Default {@link DirContext} validator that executes {@link DirContext#search(String, String, SearchControls)}. The
* name, filter and {@link SearchControls} are all configurable. There is no special handling for read only versus
* read write {@link DirContext}s.
*
* <br>
* <br>
* Configuration:
* <table border="1" summary="Configuration">
* <tr>
* <th align="left">Property</th>
* <th align="left">Description</th>
* <th align="left">Required</th>
* <th align="left">Default</th>
* </tr>
* <tr>
* <td valign="top">base</td>
* <td valign="top">
* The name parameter to the search method.
* </td>
* <td valign="top">No</td>
* <td valign="top">""</td>
* </tr>
* <tr>
* <td valign="top">filter</td>
* <td valign="top">
* The filter parameter to the search method.
* </td>
* <td valign="top">No</td>
* <td valign="top">"objectclass=*"</td>
* </tr>
* <tr>
* <td valign="top">searchControls</td>
* <td valign="top">
* The {@link SearchControls} parameter to the search method.
* </td>
* <td valign="top">No</td>
* <td valign="top">
* {@link SearchControls#setCountLimit(long)} = 1<br>
* {@link SearchControls#setReturningAttributes(String[])} = new String[] { "objectclass" }<br>
* {@link SearchControls#setTimeLimit(int)} = 500
* </td>
* </tr>
* </table>
*
* @author Eric Dalquist
*/
public class DefaultDirContextValidator implements DirContextValidator {
public static final String DEFAULT_FILTER = "objectclass=*";
private static final int DEFAULT_TIME_LIMIT = 500;
/**
* Logger for this class and sub-classes
*/
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private String base;
private String filter;
private SearchControls searchControls;
/**
* Create the default validator, creates {@link SearchControls} with search scope <code>OBJECT_SCOPE</code>,
* a countLimit of 1, returningAttributes of objectclass and timeLimit of 500.
* The default base is an empty string and the default filter is objectclass=*
*/
public DefaultDirContextValidator() {
this(SearchControls.OBJECT_SCOPE);
}
/**
* Create a validator with all the defaults of the default constructor, but with the search scope set to the
* referred value.
*
* @param searchScope The searchScope to be set in the default <code>SearchControls</code>
*/
public DefaultDirContextValidator(int searchScope) {
this.searchControls = new SearchControls();
this.searchControls.setSearchScope(searchScope);
this.searchControls.setCountLimit(1);
this.searchControls.setReturningAttributes(new String[] { "objectclass" });
this.searchControls.setTimeLimit(DEFAULT_TIME_LIMIT);
this.base = "";
this.filter = DEFAULT_FILTER;
}
/**
* @return the baseName
*/
public String getBase() {
return this.base;
}
/**
* @param base the baseName to set
*/
public void setBase(String base) {
this.base = base;
}
/**
* @return the filter
*/
public String getFilter() {
return this.filter;
}
/**
* @param filter the filter to set
*/
public void setFilter(String filter) {
if (filter == null) {
throw new IllegalArgumentException("filter may not be null");
}
this.filter = filter;
}
/**
* @return the searchControls
*/
public SearchControls getSearchControls() {
return this.searchControls;
}
/**
* @param searchControls the searchControls to set
*/
public void setSearchControls(SearchControls searchControls) {
if (searchControls == null) {
throw new IllegalArgumentException("searchControls may not be null");
}
this.searchControls = searchControls;
}
/**
* @see DirContextValidator#validateDirContext(DirContextType, DirContext)
*/
public boolean validateDirContext(DirContextType contextType, DirContext dirContext) {
Assert.notNull(contextType, "contextType may not be null");
Assert.notNull(dirContext, "dirContext may not be null");
try {
final NamingEnumeration<SearchResult> searchResults = dirContext.search(this.base, this.filter, this.searchControls);
if (searchResults.hasMore()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("DirContext '" + dirContext + "' passed validation.");
}
return true;
}
}
catch (Exception e) {
if(this.logger.isDebugEnabled()) {
this.logger.debug("DirContext '" + dirContext + "' failed validation with an exception.", e);
}
}
if (this.logger.isInfoEnabled()) {
this.logger.info("DirContext '" + dirContext + "' failed validation.");
}
return false;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.validation;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.pool2.DirContextType;
import javax.naming.directory.DirContext;
/**
* A validator for {@link DirContext}s.
*
* @author Eric Dalquist
*/
public interface DirContextValidator {
/**
* Validates the {@link DirContext}. A valid {@link DirContext} should be able
* to answer queries and if applicable write to the directory.
*
* @param contextType The type of the {@link DirContext}, refers to if {@link ContextSource#getReadOnlyContext()} or {@link ContextSource#getReadWriteContext()} was called to create the {@link DirContext}
* @param dirContext The {@link DirContext} to validate.
* @return <code>true</code> if the {@link DirContext} operated correctly during validation.
*/
boolean validateDirContext(DirContextType contextType, DirContext dirContext);
}

View File

@@ -0,0 +1,7 @@
<html>
<body>
Connection validation support for the pooling library.
</body>
</html>

View File

@@ -262,6 +262,209 @@
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="pooling2.attlist">
<xs:attribute name="max-total" type="xs:integer">
<xs:annotation>
<xs:documentation>
The overall maximum number of active connections (for all types) that can be allocated from
this pool at the same time, or non-positive for no limit. Default is -1 (no limit).
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-total-per-key" type="xs:integer">
<xs:annotation>
<xs:documentation>
The limit on the number of object instances allocated by the pool (checked out or idle),
per key. When the limit is reached, the sub-pool is said to be exhausted. A negative value
indicates no limit. Default is 8.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-idle-per-key" type="xs:integer">
<xs:annotation>
<xs:documentation>
The maximum number of active connections per type (read-only|read-write) that can remain idle in the pool,
without extra ones being released, or non-positive for no limit. Default is 8.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="min-idle-per-key" type="xs:integer">
<xs:annotation>
<xs:documentation>
The minimum number of active connections per type (read-only|read-write) that can remain
idle in the pool, without extra ones being created, or zero to create none. Default is 0.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-wait" type="xs:integer">
<xs:annotation>
<xs:documentation>
The maximum number of milliseconds that the pool will wait (when there are no available connections)
for a connection to be returned before throwing an exception, or non-positive to wait indefinitely.
Default is -1.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="block-when-exhausted" type="xs:boolean">
<xs:annotation>
<xs:documentation>
Sets to wait until a new object is available. If max-wait is positive a NoSuchElementException
is thrown if no new object is available after the maxWait time expires..
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="test-on-create" type="xs:boolean">
<xs:annotation>
<xs:documentation>
Sets whether objects created for the pool will be validated before borrowing. If the object
fails to validate, then borrowing will fail. Default is false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="test-on-borrow" type="xs:boolean">
<xs:annotation>
<xs:documentation>
The indication of whether objects will be validated before being borrowed from the pool.
If the object fails to validate, it will be dropped from the pool, and an attempt to borrow another will be made.
Default is false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="test-on-return" type="xs:boolean">
<xs:annotation>
<xs:documentation>
The indication of whether objects will be validated before being returned to the pool.
Default is false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="test-while-idle" type="xs:boolean">
<xs:annotation>
<xs:documentation>
The indication of whether objects will be validated by the idle object evictor (if any).
If an object fails to validate, it will be dropped from the pool.
Default is false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="eviction-run-interval-millis" type="xs:int">
<xs:annotation>
<xs:documentation>
The number of milliseconds to sleep between runs of the idle object evictor thread. When non-positive,
no idle object evictor thread will be run. Default is -1.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="tests-per-eviction-run" type="xs:int">
<xs:annotation>
<xs:documentation>
The number of objects to examine during each run of the idle object evictor thread (if any).
Default is 3.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="min-evictable-time-millis" type="xs:int">
<xs:annotation>
<xs:documentation>
The minimum amount of time an object may sit idle in the pool before it is eligible
for eviction by the idle object evictor (if any). Default is 1000 * 60 * 30.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="soft-min-evictable-idle-time-millis" type="xs:int">
<xs:annotation>
<xs:documentation>
The minimum amount of time an object may sit idle in the pool before it is eligible for
eviction by the idle object evictor, with the extra condition that at least minimum number
of object instances per key remain in the pool. This settings is overridden by min-evictable-time-millis if
it is set to a positive value. Default is -1.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="eviction-policy-class" type="xs:string">
<xs:annotation>
<xs:documentation>
The name of the eviction policy implementation that is used by this pool. The Pool will
attempt to load the class using the thread context class loader. If that fails, the Pool
will attempt to load the class using the class loader that loaded this class. Default is
org.apache.commons.pool2.impl.DefaultEvictionPolicy.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="fairness" type="xs:boolean">
<xs:annotation>
<xs:documentation>
Sets whether or not the pool serves threads waiting to borrow connections fairly.
True means that waiting threads are served as if waiting in a FIFO queue. Default is false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="jmx-enable" type="xs:boolean">
<xs:annotation>
<xs:documentation>
Sets whether JMX will be enabled with the platform MBean server for the pool. Default
is true.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="jmx-name-base" type="xs:string">
<xs:annotation>
<xs:documentation>
The value of the JMX name base that will be used as part of the name assigned
to JMX enabled pools. Default is null.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="jmx-name-prefix" type="xs:string">
<xs:annotation>
<xs:documentation>
The value of the JMX name prefix that will be used as part of the name assigned
to JMX enabled pools. Default value is pool.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="lifo" type="xs:boolean">
<xs:annotation>
<xs:documentation>
Sets whether the pool has LIFO (last in, first out) behaviour with
respect to idle objects - always returning the most recently used object
from the pool, or as a FIFO (first in, first out) queue, where the pool
always returns the oldest object in the idle object pool. Default is true.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validation-query-base" type="xs:string">
<xs:annotation>
<xs:documentation>
The base dn to use for validation searches. Default is LdapUtils.emptyPath().
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validation-query-filter" type="xs:string">
<xs:annotation>
<xs:documentation>
The filter to use for validation queries. Default is (objectclass=*).
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validation-query-search-controls-ref" type="xs:token">
<xs:annotation>
<xs:documentation>
Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="non-transient-exceptions" type="xs:string">
<xs:annotation>
<xs:documentation>
Id of the SearchControls instance to use for searches. Default is searchScope=OBJECT_SCOPE;
countLimit: 1; timeLimit: 500; returningAttributes: [objectclass].
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="context-source">
<xs:annotation>
<xs:documentation>
@@ -269,18 +472,32 @@
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element name="pooling">
<xs:annotation>
<xs:documentation>
Defines the settings to use for the Spring LDAP connection pooling support.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="ldap:pooling.attlist" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:choice minOccurs="0" maxOccurs="1">
<xs:sequence>
<xs:element name="pooling">
<xs:annotation>
<xs:documentation>
Defines the settings to use for the Spring LDAP connection pooling support.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="ldap:pooling.attlist" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:sequence>
<xs:element name="pooling2">
<xs:annotation>
<xs:documentation>
Defines the settings to use for the Spring LDAP connection pooling support based on commons-pool2 library.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="ldap:pooling2.attlist" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:choice>
<xs:attributeGroup ref="ldap:context-source.attlist" />
</xs:complexType>
</xs:element>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2005-2013 the original author or authors.
* Copyright 2005-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.ldap.config;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -27,6 +28,7 @@ import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.pool.factory.PoolingContextSource;
import org.springframework.ldap.pool.validation.DefaultDirContextValidator;
import org.springframework.ldap.pool2.factory.PooledContextSource;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.ldap.transaction.compensating.TempEntryRenamingStrategy;
import org.springframework.ldap.transaction.compensating.manager.ContextSourceAndDataSourceTransactionManager;
@@ -36,9 +38,12 @@ import org.springframework.ldap.transaction.compensating.support.DefaultTempEntr
import org.springframework.ldap.transaction.compensating.support.DifferentSubtreeTempEntryRenamingStrategy;
import org.springframework.transaction.PlatformTransactionManager;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.CannotProceedException;
import javax.naming.CommunicationException;
import javax.naming.directory.SearchControls;
import java.lang.management.ManagementFactory;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
@@ -124,6 +129,7 @@ public class LdapTemplateNamespaceHandlerTest {
assertSame(authenticationStrategy, getInternalState(contextSource, "authenticationStrategy"));
assertEquals(baseEnv, getInternalState(contextSource, "baseEnv"));
}
@Test
public void verifyParseWithCustomValues() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-values.xml");
@@ -360,4 +366,132 @@ public class LdapTemplateNamespaceHandlerTest {
assertNotNull(repository);
}
@Test
public void verifyParsePooling2Defaults() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pooling2-defaults.xml");
ContextSource outerContextSource = ctx.getBean(ContextSource.class);
assertNotNull(outerContextSource);
assertTrue(outerContextSource instanceof TransactionAwareContextSourceProxy);
ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
assertNotNull(pooledContextSource);
assertTrue(pooledContextSource instanceof PooledContextSource);
assertNotNull(getInternalState(pooledContextSource, "poolConfig"));
Object objectFactory = getInternalState(pooledContextSource, "dirContextPooledObjectFactory");
assertNotNull(getInternalState(objectFactory, "contextSource"));
assertNull(getInternalState(objectFactory, "dirContextValidator"));
Set<Class<? extends Throwable>> nonTransientExceptions =
(Set<Class<? extends Throwable>>) getInternalState(objectFactory, "nonTransientExceptions");
assertEquals(1, nonTransientExceptions.size());
assertTrue(nonTransientExceptions.contains(CommunicationException.class));
org.apache.commons.pool2.impl.GenericKeyedObjectPool objectPool =
(org.apache.commons.pool2.impl.GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
assertEquals(8, objectPool.getMaxIdlePerKey());
assertEquals(-1, objectPool.getMaxTotal());
assertEquals(8, objectPool.getMaxTotalPerKey());
assertEquals(0, objectPool.getMinIdlePerKey());
assertEquals(true, objectPool.getBlockWhenExhausted());
assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME, objectPool.getEvictionPolicyClassName());
assertEquals(false, objectPool.getFairness());
// ensures the pool is registered
ObjectName oname = objectPool.getJmxName();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> result = mbs.queryNames(oname, null);
assertEquals(1, result.size());
assertEquals(true, objectPool.getLifo());
assertEquals(-1L, objectPool.getMaxWaitMillis());
assertEquals(1000L*60L*30L, objectPool.getMinEvictableIdleTimeMillis());
assertEquals(3, objectPool.getNumTestsPerEvictionRun());
assertEquals(-1L, objectPool.getSoftMinEvictableIdleTimeMillis());
assertEquals(-1L, objectPool.getTimeBetweenEvictionRunsMillis());
assertEquals(false, objectPool.getTestOnBorrow());
assertEquals(false, objectPool.getTestOnCreate());
assertEquals(false, objectPool.getTestOnReturn());
assertEquals(false, objectPool.getTestWhileIdle());
}
@Test
public void verifyParsePool2SizeSet() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pool2-configured-poolsize.xml");
ContextSource outerContextSource = ctx.getBean(ContextSource.class);
assertNotNull(outerContextSource);
ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
assertNotNull(pooledContextSource);
org.apache.commons.pool2.impl.GenericKeyedObjectPool objectPool =
(org.apache.commons.pool2.impl.GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
assertEquals(12, objectPool.getMaxTotal());
assertEquals(20, objectPool.getMaxIdlePerKey());
assertEquals(10, objectPool.getMaxTotalPerKey());
assertEquals(13, objectPool.getMaxWaitMillis());
assertEquals(14, objectPool.getMinIdlePerKey());
assertEquals(true, objectPool.getBlockWhenExhausted());
assertEquals("org.springframework.ldap.pool2.DummyEvictionPolicy", objectPool.getEvictionPolicyClassName());
assertEquals(true, objectPool.getFairness());
assertEquals(false, objectPool.getLifo());
// ensures the pool is registered
ObjectName oname = objectPool.getJmxName();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> result = mbs.queryNames(oname, null);
assertEquals(1, result.size());
assertEquals("org.springframework.ldap.pool2:type=ldap-pool,name=test-pool", oname.toString());
}
@Test
public void verifyParsePool2ValidationSet() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/ldap-namespace-config-pool2-test-specified.xml");
ContextSource outerContextSource = ctx.getBean(ContextSource.class);
assertNotNull(outerContextSource);
ContextSource pooledContextSource = ((TransactionAwareContextSourceProxy) outerContextSource).getTarget();
assertNotNull(pooledContextSource);
org.apache.commons.pool2.impl.GenericKeyedObjectPool objectPool =
(org.apache.commons.pool2.impl.GenericKeyedObjectPool) getInternalState(pooledContextSource, "keyedObjectPool");
assertEquals(123, objectPool.getMinEvictableIdleTimeMillis());
assertEquals(321, objectPool.getTimeBetweenEvictionRunsMillis());
assertEquals(22, objectPool.getNumTestsPerEvictionRun());
assertEquals(12, objectPool.getSoftMinEvictableIdleTimeMillis());
assertEquals(true, objectPool.getTestOnBorrow());
assertEquals(true, objectPool.getTestOnReturn());
assertEquals(true, objectPool.getTestOnCreate());
assertEquals(true, objectPool.getTestWhileIdle());
Object objectFactory = getInternalState(pooledContextSource, "dirContextPooledObjectFactory");
org.springframework.ldap.pool2.validation.DefaultDirContextValidator validator =
(org.springframework.ldap.pool2.validation.DefaultDirContextValidator) getInternalState(objectFactory, "dirContextValidator");
assertEquals("ou=test", validator.getBase());
assertEquals("objectclass=person", validator.getFilter());
SearchControls searchControls = ctx.getBean(SearchControls.class);
assertEquals("objectclass=person", validator.getFilter());
assertSame(searchControls, validator.getSearchControls());
Set<Class<? extends Throwable>> nonTransientExceptions =
(Set<Class<? extends Throwable>>) getInternalState(objectFactory, "nonTransientExceptions");
assertEquals(2, nonTransientExceptions.size());
assertTrue(nonTransientExceptions.contains(CommunicationException.class));
assertTrue(nonTransientExceptions.contains(CannotProceedException.class));
}
@Test(expected = BeansException.class)
public void verifyParseWithPool2AndNativePoolingWillFail() {
new ClassPathXmlApplicationContext("/ldap-namespace-config-pool2-with-native.xml");
}
@Test(expected = BeansException.class)
public void verifyParseWithPool1AndPool2WillFail() {
new ClassPathXmlApplicationContext("/ldap-namespace-config-pool2-with-pool1.xml");
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.junit.Before;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.pool2.validation.DirContextValidator;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
import static org.mockito.Mockito.mock;
/**
* Contains mocks common to many tests for the connection pool.
*
* @author Ulrik Sandberg
*/
public abstract class AbstractPoolTestCase {
protected Context contextMock;
protected DirContext dirContextMock;
protected LdapContext ldapContextMock;
protected KeyedObjectPool keyedObjectPoolMock;
protected ContextSource contextSourceMock;
protected DirContextValidator dirContextValidatorMock;
@Before
public void setUp() throws Exception {
contextMock = mock(Context.class);
dirContextMock = mock(DirContext.class);
ldapContextMock = mock(LdapContext.class);
keyedObjectPoolMock = mock(KeyedObjectPool.class);
contextSourceMock = mock(ContextSource.class);
dirContextValidatorMock = mock(DirContextValidator.class);
}
}

View File

@@ -0,0 +1,409 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.junit.Test;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Eric Dalquist <a
* href="mailto:eric.dalquist@doit.wisc.edu">eric.dalquist@doit.wisc.edu</a>
*/
public class DelegatingContextTest extends AbstractPoolTestCase {
@Test
public void testConstructorAssertions() {
try {
new DelegatingContext(null, contextMock, DirContextType.READ_ONLY);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
new DelegatingContext(keyedObjectPoolMock, null,
DirContextType.READ_ONLY);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
new DelegatingContext(keyedObjectPoolMock, contextMock, null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testHelperMethods() throws Exception {
// Wrap the Context once
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
final Context delegateContext = delegatingContext.getDelegateContext();
assertEquals(contextMock, delegateContext);
final Context innerDelegateContext = delegatingContext
.getInnermostDelegateContext();
assertEquals(contextMock, innerDelegateContext);
delegatingContext.assertOpen();
// Wrap the wrapper
KeyedObjectPool secondKeyedObjectPoolMock = mock(KeyedObjectPool.class);
final DelegatingContext delegatingContext2 = new DelegatingContext(
secondKeyedObjectPoolMock, delegatingContext,
DirContextType.READ_ONLY);
final Context delegateContext2 = delegatingContext2
.getDelegateContext();
assertEquals(delegatingContext, delegateContext2);
final Context innerDelegateContext2 = delegatingContext2
.getInnermostDelegateContext();
assertEquals(contextMock, innerDelegateContext2);
delegatingContext2.assertOpen();
// Close the outer wrapper
delegatingContext2.close();
final Context delegateContext2closed = delegatingContext2
.getDelegateContext();
assertNull(delegateContext2closed);
final Context innerDelegateContext2closed = delegatingContext2
.getInnermostDelegateContext();
assertNull(innerDelegateContext2closed);
try {
delegatingContext2.assertOpen();
fail("delegatingContext2.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
// Close the outer wrapper
delegatingContext.close();
final Context delegateContextclosed = delegatingContext
.getDelegateContext();
assertNull(delegateContextclosed);
final Context innerDelegateContextclosed = delegatingContext
.getInnermostDelegateContext();
assertNull(innerDelegateContextclosed);
try {
delegatingContext.assertOpen();
fail("delegatingContext.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, contextMock);
verify(secondKeyedObjectPoolMock)
.returnObject(DirContextType.READ_ONLY, contextMock);
}
@Test
public void testObjectMethods() throws Exception {
// Wrap the Context once
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
assertEquals(contextMock.toString(), delegatingContext.toString());
delegatingContext.hashCode(); // Run it to make sure it doesn't fail
assertTrue(delegatingContext.equals(delegatingContext));
assertFalse(delegatingContext.equals(new Object()));
final DelegatingContext delegatingContext2 = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
assertTrue(delegatingContext.equals(delegatingContext2));
assertTrue(delegatingContext2.equals(delegatingContext));
assertTrue(delegatingContext.equals(contextMock));
// Close the contextMock and try again
delegatingContext.close();
assertEquals("Context is closed", delegatingContext.toString());
assertEquals(0, delegatingContext.hashCode()); // Run it to make sure
// it doesn't fail
assertTrue(delegatingContext.equals(delegatingContext));
assertFalse(delegatingContext.equals(new Object()));
assertFalse(delegatingContext.equals(delegatingContext2));
assertFalse(delegatingContext2.equals(delegatingContext));
assertFalse(delegatingContext.equals(contextMock));
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, contextMock);
}
@Test
public void testUnsupportedMethods() throws Exception {
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
try {
delegatingContext.addToEnvironment(null, null);
fail("DelegatingContext.addToEnvironment Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingContext.createSubcontext((Name) null);
fail("DelegatingContext.createSubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingContext.createSubcontext((String) null);
fail("DelegatingContext.createSubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingContext.destroySubcontext((Name) null);
fail("DelegatingContext.destroySubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingContext.destroySubcontext((String) null);
fail("DelegatingContext.destroySubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingContext.removeFromEnvironment(null);
fail("DelegatingContext.removeFromEnvironment Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
}
@Test
public void testAllMethodsOpened() throws Exception {
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
delegatingContext.bind((Name) null, null);
delegatingContext.bind((String) null, null);
delegatingContext.composeName((Name) null, (Name) null);
delegatingContext.composeName((String) null, (String) null);
delegatingContext.getEnvironment();
delegatingContext.getNameInNamespace();
delegatingContext.getNameParser((Name) null);
delegatingContext.getNameParser((String) null);
delegatingContext.list((Name) null);
delegatingContext.list((String) null);
delegatingContext.listBindings((Name) null);
delegatingContext.listBindings((String) null);
delegatingContext.lookup((Name) null);
delegatingContext.lookup((String) null);
delegatingContext.lookupLink((Name) null);
delegatingContext.lookupLink((String) null);
delegatingContext.rebind((Name) null, null);
delegatingContext.rebind((String) null, null);
delegatingContext.rename((Name) null, (Name) null);
delegatingContext.rename((String) null, (String) null);
delegatingContext.unbind((Name) null);
delegatingContext.unbind((String) null);
}
@Test
public void testAllMethodsClosed() throws Exception {
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
delegatingContext.close();
try {
delegatingContext.bind((Name) null, null);
fail("DelegatingContext.bind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.bind((String) null, null);
fail("DelegatingContext.bind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.composeName((Name) null, (Name) null);
fail("DelegatingContext.composeName should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.composeName((String) null, (String) null);
fail("DelegatingContext.composeName should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.getEnvironment();
fail("DelegatingContext.getEnvironment should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.getNameInNamespace();
fail("DelegatingContext.getNameInNamespace should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.getNameParser((Name) null);
fail("DelegatingContext.getNameParser should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.getNameParser((String) null);
fail("DelegatingContext.getNameParser should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.list((Name) null);
fail("DelegatingContext.list should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.list((String) null);
fail("DelegatingContext.list should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.listBindings((Name) null);
fail("DelegatingContext.listBindings should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.listBindings((String) null);
fail("DelegatingContext.listBindings should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.lookup((Name) null);
fail("DelegatingContext.lookup should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.lookup((String) null);
fail("DelegatingContext.lookup should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.lookupLink((Name) null);
fail("DelegatingContext.lookupLink should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.lookupLink((String) null);
fail("DelegatingContext.lookupLink should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.rebind((Name) null, null);
fail("DelegatingContext.rebind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.rebind((String) null, null);
fail("DelegatingContext.rebind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.rename((Name) null, (Name) null);
fail("DelegatingContext.rename should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.rename((String) null, (String) null);
fail("DelegatingContext.rename should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.unbind((Name) null);
fail("DelegatingContext.unbind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingContext.unbind((String) null);
fail("DelegatingContext.unbind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, contextMock);
}
@Test
public void testDoubleClose() throws Exception {
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
delegatingContext.close();
// noop close
delegatingContext.close();
verify(keyedObjectPoolMock, times(1)).returnObject(DirContextType.READ_ONLY, contextMock);
}
@Test
public void testPoolExceptionOnClose() throws Exception {
doThrow(new Exception("Fake Pool returnObject Exception"))
.when(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, contextMock);
final DelegatingContext delegatingContext = new DelegatingContext(
keyedObjectPoolMock, contextMock, DirContextType.READ_ONLY);
try {
delegatingContext.close();
fail("DelegatingContext.close should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
}
}

View File

@@ -0,0 +1,381 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.junit.Test;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Eric Dalquist <a
* href="mailto:eric.dalquist@doit.wisc.edu">eric.dalquist@doit.wisc.edu</a>
*/
public class DelegatingDirContextTest extends AbstractPoolTestCase {
@Test
public void testConstructorAssertions() {
try {
new DelegatingDirContext(keyedObjectPoolMock, null,
DirContextType.READ_ONLY);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
new DelegatingDirContext(keyedObjectPoolMock, dirContextMock, null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testHelperMethods() throws Exception {
// Wrap the DirContext once
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
final Context delegateContext = delegatingDirContext
.getDelegateContext();
assertEquals(dirContextMock, delegateContext);
final DirContext delegateDirContext = delegatingDirContext
.getDelegateDirContext();
assertEquals(dirContextMock, delegateDirContext);
final DirContext innerDelegateDirContext = delegatingDirContext
.getInnermostDelegateDirContext();
assertEquals(dirContextMock, innerDelegateDirContext);
delegatingDirContext.assertOpen();
// Wrap the wrapper
KeyedObjectPool secondKeyedObjectPoolMock = mock(KeyedObjectPool.class);
final DelegatingDirContext delegatingDirContext2 = new DelegatingDirContext(
secondKeyedObjectPoolMock, delegatingDirContext,
DirContextType.READ_ONLY);
final DirContext delegateDirContext2 = delegatingDirContext2
.getDelegateDirContext();
assertEquals(delegatingDirContext, delegateDirContext2);
final DirContext innerDelegateDirContext2 = delegatingDirContext2
.getInnermostDelegateDirContext();
assertEquals(dirContextMock, innerDelegateDirContext2);
delegatingDirContext2.assertOpen();
// Close the outer wrapper
delegatingDirContext2.close();
final DirContext delegateContext2closed = delegatingDirContext2
.getDelegateDirContext();
assertNull(delegateContext2closed);
final DirContext innerDelegateContext2closed = delegatingDirContext2
.getInnermostDelegateDirContext();
assertNull(innerDelegateContext2closed);
try {
delegatingDirContext2.assertOpen();
fail("delegatingDirContext2.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
// Close the outer wrapper
delegatingDirContext.close();
final DirContext delegateDirContextClosed = delegatingDirContext
.getDelegateDirContext();
assertNull(delegateDirContextClosed);
final DirContext innerDelegateDirContextClosed = delegatingDirContext
.getInnermostDelegateDirContext();
assertNull(innerDelegateDirContextClosed);
try {
delegatingDirContext.assertOpen();
fail("delegatingDirContext.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(secondKeyedObjectPoolMock)
.returnObject(DirContextType.READ_ONLY, dirContextMock);
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, dirContextMock);
}
@Test
public void testObjectMethods() throws Exception {
// Wrap the DirContext once
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
assertEquals(dirContextMock.toString(), delegatingDirContext.toString());
delegatingDirContext.hashCode(); // Run it to make sure it doesn't
// fail
assertTrue(delegatingDirContext.equals(delegatingDirContext));
assertFalse(delegatingDirContext.equals(new Object()));
final DelegatingDirContext delegatingDirContext2 = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
assertTrue(delegatingDirContext.equals(delegatingDirContext2));
assertTrue(delegatingDirContext2.equals(delegatingDirContext));
assertTrue(delegatingDirContext.equals(dirContextMock));
// Close the context and try again
delegatingDirContext.close();
assertEquals("DirContext is closed", delegatingDirContext.toString());
assertEquals(0, delegatingDirContext.hashCode()); // Run it to make
// sure it doesn't
// fail
assertTrue(delegatingDirContext.equals(delegatingDirContext));
assertFalse(delegatingDirContext.equals(new Object()));
assertFalse(delegatingDirContext.equals(delegatingDirContext2));
assertFalse(delegatingDirContext2.equals(delegatingDirContext));
assertFalse(delegatingDirContext.equals(dirContextMock));
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, dirContextMock);
}
@Test
public void testUnsupportedMethods() throws Exception {
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
try {
delegatingDirContext.createSubcontext((Name) null, null);
fail("DelegatingDirContext.createSubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingDirContext.createSubcontext((String) null, null);
fail("DelegatingDirContext.createSubcontext Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingDirContext.getSchema((Name) null);
fail("DelegatingDirContext.getSchema Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingDirContext.getSchema((String) null);
fail("DelegatingDirContext.getSchema Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingDirContext.getSchemaClassDefinition((Name) null);
fail("DelegatingDirContext.getSchemaClassDefinition Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingDirContext.getSchemaClassDefinition((String) null);
fail("DelegatingDirContext.getSchemaClassDefinition Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
}
@Test
public void testAllMethodsOpened() throws Exception {
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
delegatingDirContext.bind((Name) null, null, null);
delegatingDirContext.bind((String) null, null, null);
delegatingDirContext.getAttributes((Name) null, null);
delegatingDirContext.getAttributes((Name) null);
delegatingDirContext.getAttributes((String) null, null);
delegatingDirContext.getAttributes((String) null);
delegatingDirContext.modifyAttributes((Name) null, 0, null);
delegatingDirContext.modifyAttributes((Name) null, null);
delegatingDirContext.modifyAttributes((String) null, 0, null);
delegatingDirContext.modifyAttributes((String) null, null);
delegatingDirContext.rebind((Name) null, null, null);
delegatingDirContext.rebind((String) null, null, null);
delegatingDirContext.search((Name) null, (Attributes) null, null);
delegatingDirContext.search((Name) null, null);
delegatingDirContext.search((Name) null, null, null, null);
delegatingDirContext.search((Name) null, (String) null, null);
delegatingDirContext.search((String) null, (Attributes) null, null);
delegatingDirContext.search((String) null, null);
delegatingDirContext.search((String) null, null, null, null);
delegatingDirContext.search((String) null, (String) null, null);
}
@Test
public void testAllMethodsClosed() throws Exception {
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
delegatingDirContext.close();
try {
delegatingDirContext.bind((Name) null, null, null);
fail("DelegatingDirContext.bind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.bind((String) null, null, null);
fail("DelegatingDirContext.bind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.getAttributes((Name) null, null);
fail("DelegatingDirContext.getAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.getAttributes((Name) null);
fail("DelegatingDirContext.getAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.getAttributes((String) null, null);
fail("DelegatingDirContext.getAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.getAttributes((String) null);
fail("DelegatingDirContext.getAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.modifyAttributes((Name) null, 0, null);
fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.modifyAttributes((Name) null, null);
fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.modifyAttributes((String) null, 0, null);
fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.modifyAttributes((String) null, null);
fail("DelegatingDirContext.modifyAttributes should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.rebind((Name) null, null, null);
fail("DelegatingDirContext.rebind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.rebind((String) null, null, null);
fail("DelegatingDirContext.rebind should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((Name) null, (Attributes) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((Name) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((Name) null, null, null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((Name) null, (String) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((String) null, (Attributes) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((String) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((String) null, null, null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingDirContext.search((String) null, (String) null, null);
fail("DelegatingDirContext.search should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, dirContextMock);
}
@Test
public void testDoubleClose() throws Exception {
final DelegatingDirContext delegatingDirContext = new DelegatingDirContext(
keyedObjectPoolMock, dirContextMock, DirContextType.READ_ONLY);
delegatingDirContext.close();
// noop close
delegatingDirContext.close();
verify(keyedObjectPoolMock, times(1)).returnObject(DirContextType.READ_ONLY, dirContextMock);
}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.KeyedObjectPool;
import org.junit.Test;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Eric Dalquist <a
* href="mailto:eric.dalquist@doit.wisc.edu">eric.dalquist@doit.wisc.edu</a>
*/
public class DelegatingLdapContextTest extends AbstractPoolTestCase {
@Test
public void testConstructorAssertions() {
try {
new DelegatingLdapContext(keyedObjectPoolMock, null,
DirContextType.READ_ONLY);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
new DelegatingLdapContext(keyedObjectPoolMock, ldapContextMock,
null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testHelperMethods() throws Exception {
// Wrap the LdapContext once
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
final DirContext delegateDirContext = delegatingLdapContext
.getDelegateDirContext();
assertEquals(ldapContextMock, delegateDirContext);
final LdapContext delegateLdapContext = delegatingLdapContext
.getDelegateLdapContext();
assertEquals(ldapContextMock, delegateLdapContext);
final LdapContext innerDelegateLdapContext = delegatingLdapContext
.getInnermostDelegateLdapContext();
assertEquals(ldapContextMock, innerDelegateLdapContext);
delegatingLdapContext.assertOpen();
// Wrap the wrapper
KeyedObjectPool secondKeyedObjectPoolMock = mock(KeyedObjectPool.class);
final DelegatingLdapContext delegatingLdapContext2 = new DelegatingLdapContext(
secondKeyedObjectPoolMock, delegatingLdapContext,
DirContextType.READ_ONLY);
final LdapContext delegateLdapContext2 = delegatingLdapContext2
.getDelegateLdapContext();
assertEquals(delegatingLdapContext, delegateLdapContext2);
final LdapContext innerDelegateLdapContext2 = delegatingLdapContext2
.getInnermostDelegateLdapContext();
assertEquals(ldapContextMock, innerDelegateLdapContext2);
delegatingLdapContext2.assertOpen();
// Close the outer wrapper
delegatingLdapContext2.close();
final LdapContext delegateContext2closed = delegatingLdapContext2
.getDelegateLdapContext();
assertNull(delegateContext2closed);
final LdapContext innerDelegateContext2closed = delegatingLdapContext2
.getInnermostDelegateLdapContext();
assertNull(innerDelegateContext2closed);
try {
delegatingLdapContext2.assertOpen();
fail("delegatingLdapContext2.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
// Close the outer wrapper
delegatingLdapContext.close();
final LdapContext delegateLdapContextClosed = delegatingLdapContext
.getDelegateLdapContext();
assertNull(delegateLdapContextClosed);
final LdapContext innerDelegateLdapContextClosed = delegatingLdapContext
.getInnermostDelegateLdapContext();
assertNull(innerDelegateLdapContextClosed);
try {
delegatingLdapContext.assertOpen();
fail("delegatingLdapContext.assertOpen() should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(secondKeyedObjectPoolMock)
.returnObject(DirContextType.READ_ONLY, ldapContextMock);
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, ldapContextMock);
}
@Test
public void testObjectMethods() throws Exception {
// Wrap the LdapContext once
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
assertEquals(ldapContextMock.toString(),
delegatingLdapContext.toString());
delegatingLdapContext.hashCode(); // Run it to make sure it doesn't fail
assertTrue(delegatingLdapContext.equals(delegatingLdapContext));
assertFalse(delegatingLdapContext.equals(new Object()));
final DelegatingLdapContext delegatingLdapContext2 = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
assertTrue(delegatingLdapContext.equals(delegatingLdapContext2));
assertTrue(delegatingLdapContext2.equals(delegatingLdapContext));
assertTrue(delegatingLdapContext.equals(ldapContextMock));
// Close the context and try again
delegatingLdapContext.close();
assertEquals("LdapContext is closed", delegatingLdapContext.toString());
assertEquals(0, delegatingLdapContext.hashCode()); // Run it to make
// sure it doesn't
// fail
assertTrue(delegatingLdapContext.equals(delegatingLdapContext));
assertFalse(delegatingLdapContext.equals(new Object()));
assertFalse(delegatingLdapContext.equals(delegatingLdapContext2));
assertFalse(delegatingLdapContext2.equals(delegatingLdapContext));
assertFalse(delegatingLdapContext.equals(ldapContextMock));
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, ldapContextMock);
}
@Test
public void testUnsupportedMethods() throws Exception {
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
try {
delegatingLdapContext.newInstance(null);
fail("DelegatingLdapContext.newInstance Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingLdapContext.reconnect(null);
fail("DelegatingLdapContext.reconnect Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
try {
delegatingLdapContext.setRequestControls(null);
fail("DelegatingLdapContext.setRequestControls Should have thrown an UnsupportedOperationException");
} catch (UnsupportedOperationException uoe) {
// Expected
}
}
// nice
@Test
public void testAllMethodsOpened() throws Exception {
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
delegatingLdapContext.extendedOperation(null);
delegatingLdapContext.getConnectControls();
delegatingLdapContext.getRequestControls();
delegatingLdapContext.getResponseControls();
}
@Test
public void testAllMethodsClosed() throws Exception {
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
delegatingLdapContext.close();
try {
delegatingLdapContext.extendedOperation(null);
fail("DelegatingLdapContext.extendedOperation should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingLdapContext.getConnectControls();
fail("DelegatingLdapContext.getConnectControls should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingLdapContext.getRequestControls();
fail("DelegatingLdapContext.getRequestControls should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
try {
delegatingLdapContext.getResponseControls();
fail("DelegatingLdapContext.getResponseControls should have thrown a NamingException");
} catch (NamingException ne) {
// Expected
}
verify(keyedObjectPoolMock).returnObject(DirContextType.READ_ONLY, ldapContextMock);
}
@Test
public void testDoubleClose() throws Exception {
final DelegatingLdapContext delegatingLdapContext = new DelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
delegatingLdapContext.close();
// noop close
delegatingLdapContext.close();
verify(keyedObjectPoolMock, times(1)).returnObject(DirContextType.READ_ONLY, ldapContextMock);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.EvictionConfig;
import org.apache.commons.pool2.impl.EvictionPolicy;
/**
* A dummy {@link EvictionPolicy} implementation to test pool2 config.
*
* @author Anindya Chatterjee
* */
public class DummyEvictionPolicy implements EvictionPolicy {
/**
* @see EvictionPolicy#evict(EvictionConfig, PooledObject, int)
*
* */
@Override
public boolean evict(EvictionConfig config, PooledObject underTest, int idleCount) {
return false;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2;
import org.junit.Test;
import static org.mockito.Mockito.verify;
/**
* Unit tests for the MutableDelegatingLdapContext class.
*
* @author Ulrik Sandberg
*/
public class MutableDelegatingLdapContextTest extends AbstractPoolTestCase {
@Test
public void testSupportedMethodsAllowedToCall() throws Exception {
final MutableDelegatingLdapContext delegatingLdapContext = new MutableDelegatingLdapContext(
keyedObjectPoolMock, ldapContextMock, DirContextType.READ_ONLY);
delegatingLdapContext.setRequestControls(null);
verify(ldapContextMock).setRequestControls(null);
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.reflection.Whitebox;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.pool2.DirContextType;
import org.springframework.ldap.pool2.validation.DirContextValidator;
import org.springframework.ldap.pool2.AbstractPoolTestCase;
import javax.naming.directory.DirContext;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* @author Eric Dalquist
* @author Anindya Chatterjee
*/
public class DirContextPooledObjectFactoryTest extends AbstractPoolTestCase {
@Test
public void testProperties() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
try {
objectFactory.setContextSource(null);
fail("DirContextPooledObjectFactory.setContextSource should have thrown an IllegalArgumentException");
}
catch (IllegalArgumentException iae) {
// Expected
}
objectFactory.setContextSource(contextSourceMock);
final ContextSource contextSource2 = objectFactory.getContextSource();
assertEquals(contextSourceMock, contextSource2);
try {
objectFactory.setDirContextValidator(null);
fail("DirContextPooledObjectFactory.setDirContextValidator should have thrown an IllegalArgumentException");
}
catch (IllegalArgumentException iae) {
// Expected
}
objectFactory.setDirContextValidator(dirContextValidatorMock);
final DirContextValidator dirContextValidator2 = objectFactory.getDirContextValidator();
assertEquals(dirContextValidatorMock, dirContextValidator2);
}
@Test
public void testMakeObjectAssertions() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
try {
objectFactory.makeObject(DirContextType.READ_ONLY);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
objectFactory.setContextSource(contextSourceMock);
try {
objectFactory.makeObject(null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testMakeObjectReadOnly() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
DirContext readOnlyContextMock = mock(DirContext.class);
when(contextSourceMock.getReadOnlyContext()).thenReturn(readOnlyContextMock);
objectFactory.setContextSource(contextSourceMock);
final PooledObject createdDirContext = objectFactory.makeObject(DirContextType.READ_ONLY);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(createdDirContext.getObject());
assertEquals(readOnlyContextMock, Whitebox.getInternalState(invocationHandler, "target"));
}
@Test
public void testMakeObjectReadWrite() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
DirContext readWriteContextMock = mock(DirContext.class);
when(contextSourceMock.getReadWriteContext()).thenReturn(readWriteContextMock);
objectFactory.setContextSource(contextSourceMock);
final PooledObject createdDirContext = objectFactory.makeObject(DirContextType.READ_WRITE);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(createdDirContext.getObject());
assertEquals(readWriteContextMock, Whitebox.getInternalState(invocationHandler, "target"));
}
@Test
public void testValidateObjectAssertions() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
try {
PooledObject pooledObject = new DefaultPooledObject(dirContextMock);
objectFactory.validateObject(DirContextType.READ_ONLY, pooledObject);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
objectFactory.setDirContextValidator(dirContextValidatorMock);
try {
PooledObject pooledObject = new DefaultPooledObject(dirContextMock);
objectFactory.validateObject(null, pooledObject);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
PooledObject pooledObject = new DefaultPooledObject(dirContextMock);
objectFactory.validateObject(new Object(), pooledObject);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
objectFactory.validateObject(DirContextType.READ_ONLY, null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
PooledObject pooledObject = new DefaultPooledObject(new Object());
objectFactory.validateObject(DirContextType.READ_ONLY, pooledObject);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testValidateObject() throws Exception {
when(dirContextValidatorMock
.validateDirContext(DirContextType.READ_ONLY, dirContextMock))
.thenReturn(true);
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
objectFactory.setDirContextValidator(dirContextValidatorMock);
PooledObject pooledObject = new DefaultPooledObject(dirContextMock);
final boolean valid = objectFactory.validateObject(DirContextType.READ_ONLY, pooledObject);
assertTrue(valid);
//Check exception in validator
DirContextValidator secondDirContextValidatorMock = mock(DirContextValidator.class);
when(secondDirContextValidatorMock.validateDirContext(DirContextType.READ_ONLY, dirContextMock))
.thenThrow(new RuntimeException("Failed to validate"));
objectFactory.setDirContextValidator(secondDirContextValidatorMock);
final boolean valid2 = objectFactory.validateObject(DirContextType.READ_ONLY, pooledObject);
assertFalse(valid2);
}
@Test
public void testDestroyObjectAssertions() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
try {
objectFactory.destroyObject(DirContextType.READ_ONLY, null);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
PooledObject pooledObject = new DefaultPooledObject(new Object());
objectFactory.validateObject(DirContextType.READ_ONLY, pooledObject);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
@Test
public void testDestroyObject() throws Exception {
final DirContextPooledObjectFactory objectFactory = new DirContextPooledObjectFactory();
PooledObject pooledObject = new DefaultPooledObject(dirContextMock);
objectFactory.destroyObject(DirContextType.READ_ONLY, pooledObject);
DirContext throwingDirContextMock = Mockito.mock(DirContext.class);
doThrow(new RuntimeException("Failed to close"))
.when(throwingDirContextMock).close();
pooledObject = new DefaultPooledObject(throwingDirContextMock);
objectFactory.destroyObject(DirContextType.READ_ONLY, pooledObject);
verify(dirContextMock).close();
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.junit.Test;
import org.springframework.ldap.pool2.AbstractPoolTestCase;
import org.springframework.ldap.pool2.MutableDelegatingLdapContext;
import javax.naming.directory.DirContext;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
/**
* Unit tests for the MutablePoolingContextSource class.
*
* @author Ulrik Sandberg
* @author Anindya Chatterjee
*/
public class MutablePooledContextSourceTest extends AbstractPoolTestCase {
@Test
public void testGetReadOnlyLdapContext() throws Exception {
when(contextSourceMock.getReadOnlyContext()).thenReturn(ldapContextMock);
final MutablePooledContextSource poolingContextSource = new MutablePooledContextSource(null);
poolingContextSource.setContextSource(contextSourceMock);
// Get a context
final DirContext result = poolingContextSource.getReadOnlyContext();
assertEquals(MutableDelegatingLdapContext.class, result.getClass());
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.junit.Test;
import org.springframework.ldap.pool2.AbstractPoolTestCase;
import static org.junit.Assert.assertEquals;
/**
* @author Anindya Chatterjee
* */
public class PoolConfigTest extends AbstractPoolTestCase {
@Test
public void testProperties() {
final PoolConfig poolConfig = new PoolConfig();
poolConfig.setMaxTotalPerKey(5);
final int maxTotalPerKey = poolConfig.getMaxTotalPerKey();
assertEquals(5, maxTotalPerKey);
poolConfig.setMaxIdlePerKey(500);
final int maxIdle = poolConfig.getMaxIdlePerKey();
assertEquals(500, maxIdle);
poolConfig.setMaxTotal(5000);
final int maxTotal = poolConfig.getMaxTotal();
assertEquals(5000, maxTotal);
poolConfig.setMaxWaitMillis(2000L);
final long maxWait = poolConfig.getMaxWaitMillis();
assertEquals(2000L, maxWait);
poolConfig.setMinEvictableIdleTimeMillis(60000L);
final long minEvictableIdleTimeMillis = poolConfig.getMinEvictableIdleTimeMillis();
assertEquals(60000L, minEvictableIdleTimeMillis);
poolConfig.setMinIdlePerKey(100);
final int minIdle = poolConfig.getMinIdlePerKey();
assertEquals(100, minIdle);
poolConfig.setNumTestsPerEvictionRun(5);
final int numTestsPerEvictionRun = poolConfig.getNumTestsPerEvictionRun();
assertEquals(5, numTestsPerEvictionRun);
poolConfig.setTestOnBorrow(true);
final boolean testOnBorrow = poolConfig.isTestOnBorrow();
assertEquals(true, testOnBorrow);
poolConfig.setTestOnReturn(true);
final boolean testOnReturn = poolConfig.isTestOnReturn();
assertEquals(true, testOnReturn);
poolConfig.setTestWhileIdle(true);
final boolean testWhileIdle = poolConfig.isTestWhileIdle();
assertEquals(true, testWhileIdle);
poolConfig.setTestOnCreate(true);
final boolean testOnCreate = poolConfig.isTestOnCreate();
assertEquals(true, testOnCreate);
poolConfig.setTimeBetweenEvictionRunsMillis(120000L);
final long timeBetweenEvictionRunsMillis = poolConfig.getTimeBetweenEvictionRunsMillis();
assertEquals(120000L, timeBetweenEvictionRunsMillis);
poolConfig.setSoftMinEvictableIdleTimeMillis(120000L);
final long softMinEvictableIdleTimeMillis = poolConfig.getSoftMinEvictableIdleTimeMillis();
assertEquals(120000L, softMinEvictableIdleTimeMillis);
poolConfig.setBlockWhenExhausted(true);
final boolean whenExhaustedAction = poolConfig.isBlockWhenExhausted();
assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED, whenExhaustedAction);
poolConfig.setEvictionPolicyClassName(GenericKeyedObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME);
final String evictionPolicyClassName = poolConfig.getEvictionPolicyClassName();
assertEquals(GenericKeyedObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME, evictionPolicyClassName);
poolConfig.setFairness(true);
final boolean fairness = poolConfig.isFairness();
assertEquals(true, fairness);
poolConfig.setJmxEnabled(true);
final boolean jmxEnabled = poolConfig.isJmxEnabled();
assertEquals(true, jmxEnabled);
poolConfig.setJmxNameBase("test");
final String jmxBaseName = poolConfig.getJmxNameBase();
assertEquals("test", jmxBaseName);
poolConfig.setJmxNamePrefix("pool");
final String prefix = poolConfig.getJmxNamePrefix();
assertEquals("pool", prefix);
poolConfig.setLifo(true);
final boolean lifo = poolConfig.isLifo();
assertEquals(true, lifo);
}
}

View File

@@ -0,0 +1,237 @@
/*
* Copyright 2005-2015 the original author or authors.
*
* Licensed 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.springframework.ldap.pool2.factory;
import org.junit.Test;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.pool2.validation.DirContextValidator;
import org.springframework.ldap.pool2.AbstractPoolTestCase;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Eric Dalquist
* @author Anindya Chatterjee
*/
public class PooledContextSourceTest extends AbstractPoolTestCase {
@Test
public void testProperties() throws Exception {
final PoolConfig poolConfig = new PoolConfig();
poolConfig.setMaxIdlePerKey(500);
poolConfig.setMinIdlePerKey(100);
poolConfig.setMaxTotal(5000);
poolConfig.setMaxTotalPerKey(5);
poolConfig.setMaxWaitMillis(2000L);
poolConfig.setMinEvictableIdleTimeMillis(60000L);
poolConfig.setNumTestsPerEvictionRun(5);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
poolConfig.setTestOnCreate(true);
poolConfig.setTimeBetweenEvictionRunsMillis(120000L);
poolConfig.setSoftMinEvictableIdleTimeMillis(120000L);
poolConfig.setBlockWhenExhausted(true);
poolConfig.setFairness(true);
poolConfig.setJmxEnabled(true);
poolConfig.setJmxNameBase("test");
poolConfig.setJmxNamePrefix("pool");
poolConfig.setLifo(true);
final PooledContextSource PooledContextSource = new PooledContextSource(poolConfig);
try {
PooledContextSource.setContextSource(null);
fail("PooledContextSource.setBaseName should have thrown an IllegalArgumentException");
}
catch (IllegalArgumentException iae) {
// Expected
}
PooledContextSource.setContextSource(contextSourceMock);
final ContextSource contextSource2 = PooledContextSource.getContextSource();
assertEquals(contextSourceMock, contextSource2);
try {
PooledContextSource.setDirContextValidator(null);
fail("PooledContextSource.setDirContextValidator should have thrown an IllegalArgumentException");
}
catch (IllegalArgumentException iae) {
// Expected
}
PooledContextSource.setDirContextValidator(dirContextValidatorMock);
final DirContextValidator dirContextValidator2 = PooledContextSource.getDirContextValidator();
assertEquals(dirContextValidatorMock, dirContextValidator2);
final int numActive = PooledContextSource.getNumActive();
assertEquals(0, numActive);
final int numIdle = PooledContextSource.getNumIdle();
assertEquals(0, numIdle);
}
@Test
public void testGetReadOnlyContextPool() throws Exception {
DirContext secondDirContextMock = mock(DirContext.class);
when(contextSourceMock.getReadOnlyContext()).thenReturn(dirContextMock, secondDirContextMock);
final PooledContextSource PooledContextSource = new PooledContextSource(null);
PooledContextSource.setContextSource(contextSourceMock);
//Get a context
final DirContext readOnlyContext1 = PooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext1, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Close the context
readOnlyContext1.close();
assertEquals(0, PooledContextSource.getNumActive());
assertEquals(1, PooledContextSource.getNumIdle());
//Get the context again
final DirContext readOnlyContext2 = PooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext2, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Get a new context
final DirContext readOnlyContext3 = PooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext3, secondDirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(2, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Close context
readOnlyContext2.close();
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(1, PooledContextSource.getNumIdle());
//Close context
readOnlyContext3.close();
assertEquals(0, PooledContextSource.getNumActive());
assertEquals(2, PooledContextSource.getNumIdle());
}
@Test
public void testGetReadWriteContextPool() throws Exception {
DirContext secondDirContextMock = mock(DirContext.class);
when(contextSourceMock.getReadWriteContext()).thenReturn(dirContextMock, secondDirContextMock);
final PooledContextSource PooledContextSource = new PooledContextSource(null);
PooledContextSource.setContextSource(contextSourceMock);
//Get a context
final DirContext readOnlyContext1 = PooledContextSource.getReadWriteContext();
assertEquals(readOnlyContext1, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Close the context
readOnlyContext1.close();
assertEquals(0, PooledContextSource.getNumActive());
assertEquals(1, PooledContextSource.getNumIdle());
//Get the context again
final DirContext readOnlyContext2 = PooledContextSource.getReadWriteContext();
assertEquals(readOnlyContext2, dirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Get a new context
final DirContext readOnlyContext3 = PooledContextSource.getReadWriteContext();
assertEquals(readOnlyContext3, secondDirContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(2, PooledContextSource.getNumActive());
assertEquals(0, PooledContextSource.getNumIdle());
//Close context
readOnlyContext2.close();
assertEquals(1, PooledContextSource.getNumActive());
assertEquals(1, PooledContextSource.getNumIdle());
//Close context
readOnlyContext3.close();
assertEquals(0, PooledContextSource.getNumActive());
assertEquals(2, PooledContextSource.getNumIdle());
}
@Test
public void testGetContextException() throws Exception {
when(contextSourceMock.getReadWriteContext())
.thenThrow(new RuntimeException("Problem getting context"));
final PooledContextSource PooledContextSource = new PooledContextSource(null);
PooledContextSource.setContextSource(contextSourceMock);
try {
PooledContextSource.getReadWriteContext();
fail("PooledContextSource.getReadWriteContext should have thrown DataAccessResourceFailureException");
}
catch (DataAccessResourceFailureException darfe) {
// Expected
}
}
@Test
public void testGetReadOnlyLdapContext() throws Exception {
LdapContext secondLdapContextMock = mock(LdapContext.class);
when(contextSourceMock.getReadOnlyContext()).thenReturn(ldapContextMock, secondLdapContextMock);
final PooledContextSource pooledContextSource = new PooledContextSource(null);
pooledContextSource.setContextSource(contextSourceMock);
//Get a context
final DirContext readOnlyContext1 = pooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext1, ldapContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, pooledContextSource.getNumActive());
assertEquals(0, pooledContextSource.getNumIdle());
//Close the context
readOnlyContext1.close();
assertEquals(0, pooledContextSource.getNumActive());
assertEquals(1, pooledContextSource.getNumIdle());
//Get the context again
final DirContext readOnlyContext2 = pooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext2, ldapContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(1, pooledContextSource.getNumActive());
assertEquals(0, pooledContextSource.getNumIdle());
//Get a new context
final DirContext readOnlyContext3 = pooledContextSource.getReadOnlyContext();
assertEquals(readOnlyContext3, secondLdapContextMock); //Order reversed because the 'wrapper' has the needed equals logic
assertEquals(2, pooledContextSource.getNumActive());
assertEquals(0, pooledContextSource.getNumIdle());
//Close context
readOnlyContext2.close();
assertEquals(1, pooledContextSource.getNumActive());
assertEquals(1, pooledContextSource.getNumIdle());
//Close context
readOnlyContext3.close();
assertEquals(0, pooledContextSource.getNumActive());
assertEquals(2, pooledContextSource.getNumIdle());
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<ldap:context-source password="apassword" url="ldap://localhost:389" username="uid=admin">
<ldap:pooling2
max-total-per-key="10"
max-idle-per-key="20"
max-total="12"
max-wait="13"
min-idle-per-key="14"
block-when-exhausted="true"
eviction-policy-class="org.springframework.ldap.pool2.DummyEvictionPolicy"
fairness="true"
jmx-enable="true"
jmx-name-base="org.springframework.ldap.pool2:type=ldap-pool,name="
jmx-name-prefix="test-pool"
lifo="false" />
</ldap:context-source>
<ldap:ldap-template />
</beans>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<ldap:context-source password="apassword" url="ldap://localhost:389" username="uid=admin">
<ldap:pooling2
test-on-borrow="true"
test-on-return="true"
test-while-idle="true"
test-on-create="true"
min-evictable-time-millis="123"
eviction-run-interval-millis="321"
tests-per-eviction-run="22"
soft-min-evictable-idle-time-millis="12"
validation-query-base="ou=test"
validation-query-filter="objectclass=person"
validation-query-search-controls-ref="searchControls"
non-transient-exceptions="javax.naming.CannotProceedException,javax.naming.CommunicationException" />
</ldap:context-source>
<bean class="javax.naming.directory.SearchControls" id="searchControls" />
<ldap:ldap-template />
</beans>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<!--
The below is invalid, since native pooling is not supported together with Spring LDAP pooling.
-->
<ldap:context-source
password="apassword" url="ldap://localhost:389" username="uid=admin"
native-pooling="true">
<ldap:pooling2 />
</ldap:context-source>
<ldap:ldap-template />
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<!-- Invalid pool configuration. Only one of them can be used at the same time. -->
<ldap:context-source
password="apassword" url="ldap://localhost:389" username="uid=admin">
<ldap:pooling />
<ldap:pooling2 />
</ldap:context-source>
<ldap:ldap-template />
</beans>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<ldap:context-source password="apassword" url="ldap://localhost:389" username="uid=admin">
<ldap:pooling2 />
</ldap:context-source>
<ldap:ldap-template />
</beans>

View File

@@ -20,6 +20,7 @@ ext.mockitoVersion = '1.10.19'
ext.queryDslVersion = '3.6.3'
ext.slf4jVersion = '1.7.12'
ext.powerMockVersion = '1.6.2'
ext.commonsPool2Version = '2.4.2'
ext.powerMockDependencies = [
"org.powermock:powermock-core:$powerMockVersion",