diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDirectory.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDirectory.java
--- a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDirectory.java
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDirectory.java
@@ -50,8 +50,11 @@
 import org.nuxeo.runtime.api.Framework;
 
 /**
+ * Implementation of the Directory interface for servers implementing the
+ * Lightweight Directory Access Protocol.
+ *
  * @author ogrisel
- *
+ * @author Robert Browning
  */
 public class LDAPDirectory extends AbstractDirectory {
 
@@ -130,18 +133,25 @@
         props.put(Context.INITIAL_CONTEXT_FACTORY,
                 "com.sun.jndi.ldap.LdapCtxFactory");
 
+        /*
+         * Get inital connection URLs, dynamic URLs may cause the list to be
+         * updated when creating the session
+         */
         String ldapUrls = serverConfig.getLdapUrls();
         props.put(Context.PROVIDER_URL, ldapUrls);
         props.put(Context.REFERRAL, "follow");
 
         /*
-        * NOTE: Currently this directory service does not use SSL, however if
-        * it should then for SSL connections this value must not be set as this
-        * causes the connection to fail
-        */
+         * SSL Connections do not work with connection timeout property
+         */
         if (serverConfig.getConnectionTimeout() > -1) {
-            props.put("com.sun.jndi.ldap.connect.timeout",
-            Integer.toString(serverConfig.getConnectionTimeout()));
+            if (!serverConfig.useSsl()) {
+                props.put("com.sun.jndi.ldap.connect.timeout",
+                        Integer.toString(serverConfig.getConnectionTimeout()));
+            } else {
+                log.warn("SSL connections do not operate correctly"
+                        + " when used with the connection timeout parameter, disabling timout");
+            }
         }
 
         String bindDn = serverConfig.getBindDn();
@@ -189,6 +199,13 @@
                 LDAPReference ldapReference = (LDAPReference) reference;
                 attrs.add(ldapReference.getStaticAttributeId(fieldMapper));
                 attrs.add(ldapReference.getDynamicAttributeId());
+
+                // Add Dynamic Reference attributes filtering
+                for (LDAPDynamicReferenceDescriptor dynAtt : ldapReference.getDynamicAttributes()) {
+                    attrs.add(dynAtt.baseDN);
+                    attrs.add(dynAtt.filter);
+                }
+
             }
         }
 
@@ -223,6 +240,15 @@
 
     protected DirContext createContext() throws DirectoryException {
         try {
+            /*
+             * Dynamic server list requires re-computation on each access
+             */
+            String serverName = config.getServerName();
+            LDAPServerDescriptor serverConfig = factory.getServer(serverName);
+            if (serverConfig.isDynamicServerList()) {
+                String ldapUrls = serverConfig.getLdapUrls();
+                contextProperties.put(Context.PROVIDER_URL, ldapUrls);
+            }
             return new InitialDirContext(contextProperties);
         } catch (NamingException e) {
             log.error(e);
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDynamicReferenceDescriptor.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDynamicReferenceDescriptor.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPDynamicReferenceDescriptor.java
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Nuxeo - initial API and implementation
+ *
+ */
+package org.nuxeo.ecm.directory.ldap;
+
+import org.nuxeo.common.xmap.annotation.XNode;
+import org.nuxeo.common.xmap.annotation.XObject;
+
+/**
+ * Dynamic Reference descriptor for Ldap References. Used to setup a dynamic
+ * reference with a set of attributes instead of an url (dynamicAttributeId).
+ * 
+ * <ul>
+ * <li>filter: Attribut that contains the filter, example
+ * "msExchDynamicDLFilter"</li>
+ * <li>type: subtree/one</li>
+ * <li>baseDN: Attribut that contains the BaseDN used for search, example
+ * "msExchDynamicDLBaseDN"</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:nulrich@nuxeo.com">Nicolas Ulrich</a>
+ */
+@XObject(value = "dynamicReference")
+public class LDAPDynamicReferenceDescriptor {
+
+    @XNode("@filter")
+    public String filter = null;
+
+    @XNode("@baseDN")
+    public String baseDN = null;
+
+    @XNode("@type")
+    public String type = null;
+
+}
\ No newline at end of file
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPReference.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPReference.java
--- a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPReference.java
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPReference.java
@@ -20,11 +20,14 @@
 package org.nuxeo.ecm.directory.ldap;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 
+import javax.naming.CompositeName;
+import javax.naming.Name;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
@@ -40,6 +43,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.nuxeo.common.utils.StringUtils;
 import org.nuxeo.common.xmap.annotation.XNode;
+import org.nuxeo.common.xmap.annotation.XNodeList;
 import org.nuxeo.common.xmap.annotation.XObject;
 import org.nuxeo.ecm.core.api.ClientException;
 import org.nuxeo.ecm.directory.AbstractReference;
@@ -47,6 +51,8 @@
 import org.nuxeo.ecm.directory.DirectoryException;
 import org.nuxeo.ecm.directory.DirectoryFieldMapper;
 import org.nuxeo.ecm.directory.Session;
+import org.nuxeo.ecm.directory.ldap.filter.FilterExpressionCorrector;
+import org.nuxeo.ecm.directory.ldap.filter.FilterExpressionCorrector.FilterJobs;
 
 import com.sun.jndi.ldap.LdapURL;
 
@@ -58,9 +64,9 @@
  * <li>the static attribute strategy where a multi-valued attribute store the
  * exhaustive list of distinguished names of the refereed entries (eg. the
  * uniqueMember attribute of the groupOfUniqueNames objectclass)</li>
- * <li>the dynamic attribute strategy where a potentially multi-valued
- * attribute stores a ldap urls intensively describing the refereed LDAP entries
- * (eg. the memberURLs attribute of the groupOfURLs objectclass)</li>
+ * <li>the dynamic attribute strategy where a potentially multi-valued attribute
+ * stores a ldap urls intensively describing the refereed LDAP entries (eg. the
+ * memberURLs attribute of the groupOfURLs objectclass)</li>
  * </ul>
  *
  * Please note that both static and dynamic references are resolved in read mode
@@ -77,6 +83,9 @@
 
     private static final Log log = LogFactory.getLog(LDAPReference.class);
 
+    @XNodeList(value = "dynamicReference", type = LDAPDynamicReferenceDescriptor[].class, componentType = LDAPDynamicReferenceDescriptor.class)
+    private LDAPDynamicReferenceDescriptor[] dynamicReferences;
+
     @XNode("@forceDnConsistencyCheck")
     public boolean forceDnConsistencyCheck;
 
@@ -141,6 +150,10 @@
         }
     }
 
+    public List<LDAPDynamicReferenceDescriptor> getDynamicAttributes() {
+        return Arrays.asList(dynamicReferences);
+    }
+
     public String getDynamicAttributeId() {
         return dynamicAttributeId;
     }
@@ -573,6 +586,16 @@
                 targetSession.close();
             }
         }
+
+        /*
+         * This kind of reference is not supported because Active Directory use
+         * filter expression not yet supported by LDAPFilterMatcher. See
+         * NXP-4562
+         */
+        if (this.dynamicReferences != null && this.dynamicReferences.length > 0) {
+            log.error("This kind of reference is not supported.");
+        }
+
         return new ArrayList<String>(sourceIds);
     }
 
@@ -681,8 +704,10 @@
                                                 attributeIdsToCollect, ", "),
                                         this));
                             }
+
+                            Name name = new CompositeName().add(targetDn);
                             entry = targetSession.dirContext.getAttributes(
-                                    targetDn, attributeIdsToCollect);
+                                    name, attributeIdsToCollect);
                         } catch (NamingException e) {
                             log.warn(String.format(
                                     "could not find target '%s' while fetching reference '%s'",
@@ -748,54 +773,60 @@
                         // upperlevel than directory elements
                         continue;
                     } else {
-                        // combine the ldapUrl search query with target
-                        // directory own constraints
-                        SearchControls scts = new SearchControls();
 
-                        // use the most specific scope
-                        scts.setSearchScope(Math.min(scope,
-                                targetDirconfig.getSearchScope()));
+                        // Search for references elements
+                        targetIds.addAll(getReferencedElements(attributes,
+                                directoryDn, linkDn, ldapUrl.getFilter(), scope));
 
-                        // only fetch the ids of the targets
-                        scts.setReturningAttributes(new String[] { targetSession.idAttribute });
-
-                        // combine the filter of the target directory with the
-                        // provided filter if any
-                        String filter = ldapUrl.getFilter();
-                        String targetFilter = targetDirconfig.getSearchFilter();
-                        if (filter == null || filter.length() == 0) {
-                            filter = targetFilter;
-                        } else if (targetFilter != null
-                                && targetFilter.length() > 0) {
-                            filter = String.format("(&(%s)(%s))", targetFilter,
-                                    filter);
-                        }
-
-                        // perform the request and collect the ids
-                        if (log.isDebugEnabled()) {
-                            log.debug(String.format(
-                                    "LDAPReference.getLdapTargetIds(%s): LDAP search dn='%s' "
-                                            + " filter='%s' scope='%s' [%s]",
-                                    attributes, linkDn, filter,
-                                    scts.getSearchScope(), this));
-                        }
-                        NamingEnumeration<SearchResult> results = targetSession.dirContext.search(
-                                linkDn, filter, scts);
-                        while (results.hasMore()) {
-                            // NXP-2461: check that id field is filled
-                            Attribute attr = results.next().getAttributes().get(
-                                    targetSession.idAttribute);
-                            if (attr != null) {
-                                String collectedId = attr.get().toString();
-                                if (collectedId != null) {
-                                    targetIds.add(collectedId);
-                                }
-                            }
-
-                        }
                     }
                 }
             }
+
+            if (this.dynamicReferences != null
+                    && this.dynamicReferences.length > 0) {
+
+                // Only the first Dynamic Reference is used
+                LDAPDynamicReferenceDescriptor dynAtt = this.dynamicReferences[0];
+
+                Attribute baseDnsAttribute = attributes.get(dynAtt.baseDN);
+                Attribute filterAttribute = attributes.get(dynAtt.filter);
+
+                if (baseDnsAttribute != null && filterAttribute != null) {
+
+                    // Get the BaseDN value from the descriptor
+                    NamingEnumeration<?> baseDns = baseDnsAttribute.getAll();
+                    String linkDnValue = baseDns.next().toString();
+                    linkDnValue = pseudoNormalizeDn(linkDnValue);
+
+                    // Get the filter value from the descriptor
+                    NamingEnumeration<?> filters = filterAttribute.getAll();
+                    String filterValue = filters.next().toString();
+
+                    // Get the scope value from the descriptor
+                    int scope = "subtree".equalsIgnoreCase(dynAtt.type) ? SearchControls.SUBTREE_SCOPE
+                            : SearchControls.ONELEVEL_SCOPE;
+
+                    String directoryDn = pseudoNormalizeDn(targetDirconfig.getSearchBaseDn());
+
+                    // if the dns match, and if the link dn is pointing to
+                    // elements that at upperlevel than directory elements
+                    if ((linkDnValue.endsWith(directoryDn) || directoryDn.endsWith(linkDnValue))
+                            && !(directoryDn.endsWith(linkDnValue)
+                                    && linkDnValue.length() < directoryDn.length() && scope == SearchControls.ONELEVEL_SCOPE)) {
+
+                        // Correct the filter expression
+                        filterValue = FilterExpressionCorrector.correctFilter(
+                                filterValue, FilterJobs.CORRECT_NOT);
+
+                        // Search for references elements
+                        targetIds.addAll(getReferencedElements(attributes,
+                                directoryDn, linkDnValue, filterValue, scope));
+
+                    }
+
+                }
+
+            }
             // return merged attributes
             return new ArrayList<String>(targetIds);
         } catch (NamingException e) {
@@ -806,6 +837,80 @@
     }
 
     /**
+     * Retrieve the elements referenced by the filter/BaseDN/Scope request.
+     *
+     * @param attributes Attributes of the referencer element
+     * @param directoryDn Dn of the Directory
+     * @param linkDnValue Dn specified in the parent
+     * @param filterValue Filter expression specified in the parent
+     * @param scope scope for the search
+     * @return The list of the referenced elements.
+     * @throws DirectoryException
+     * @throws NamingException
+     */
+    private Set<String> getReferencedElements(Attributes attributes,
+            String directoryDn, String linkDn, String filter, int scope)
+            throws DirectoryException, NamingException {
+
+        Set<String> targetIds = new TreeSet<String>();
+
+        LDAPDirectoryDescriptor targetDirconfig = getTargetDirectoryDescriptor();
+        LDAPDirectory targetDirectory = (LDAPDirectory) getTargetDirectory();
+        LDAPSession targetSession = (LDAPSession) targetDirectory.getSession();
+
+        // use the most specific scope between the one specified in the
+        // Directory and the specified in the Parent
+        String dn = directoryDn.endsWith(linkDn)
+                && directoryDn.length() > linkDn.length() ? directoryDn
+                : linkDn;
+
+        // combine the ldapUrl search query with target
+        // directory own constraints
+        SearchControls scts = new SearchControls();
+
+        // use the most specific scope
+        scts.setSearchScope(Math.min(scope, targetDirconfig.getSearchScope()));
+
+        // only fetch the ids of the targets
+        scts.setReturningAttributes(new String[] { targetSession.idAttribute });
+
+        // combine the filter of the target directory with the
+        // provided filter if any
+        String targetFilter = targetDirconfig.getSearchFilter();
+        if (filter == null || filter.length() == 0) {
+            filter = targetFilter;
+        } else if (targetFilter != null && targetFilter.length() > 0) {
+            filter = String.format("(&(%s)(%s))", targetFilter, filter);
+        }
+
+        // perform the request and collect the ids
+        if (log.isDebugEnabled()) {
+            log.debug(String.format(
+                    "LDAPReference.getLdapTargetIds(%s): LDAP search dn='%s' "
+                            + " filter='%s' scope='%s' [%s]", attributes, dn,
+                    dn, scts.getSearchScope(), this));
+        }
+
+        Name name = new CompositeName().add(dn);
+        NamingEnumeration<SearchResult> results = targetSession.dirContext.search(
+                name, filter, scts);
+        while (results.hasMore()) {
+            // NXP-2461: check that id field is filled
+            Attribute attr = results.next().getAttributes().get(
+                    targetSession.idAttribute);
+            if (attr != null) {
+                String collectedId = attr.get().toString();
+                if (collectedId != null) {
+                    targetIds.add(collectedId);
+                }
+            }
+
+        }
+
+        return targetIds;
+    }
+
+    /**
      * Remove existing statically defined links for the given source id (dynamic
      * references remain unaltered)
      *
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPServerDescriptor.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPServerDescriptor.java
--- a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPServerDescriptor.java
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPServerDescriptor.java
@@ -16,19 +16,42 @@
  *
  * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
  */
-
 package org.nuxeo.ecm.directory.ldap;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.directory.shared.ldap.name.LdapDN;
 import org.nuxeo.common.xmap.annotation.XNode;
 import org.nuxeo.common.xmap.annotation.XNodeList;
 import org.nuxeo.common.xmap.annotation.XObject;
 import org.nuxeo.ecm.directory.DirectoryException;
+import org.nuxeo.ecm.directory.ldap.dns.DNSServiceEntry;
+import org.nuxeo.ecm.directory.ldap.dns.DNSServiceResolver;
+import org.nuxeo.ecm.directory.ldap.dns.DNSServiceResolverImpl;
 
+import com.sun.jndi.ldap.LdapURL;
 
 @XObject(value = "server")
 public class LDAPServerDescriptor {
 
+    public static final Log log = LogFactory.getLog(LDAPServerDescriptor.class);
+
+    protected static final String LDAPS_SCHEME = "ldaps";
+
+    protected static final String LDAP_SCHEME = "ldap";
+
     @XNode("@name")
     public String name;
 
@@ -42,6 +65,18 @@
     @XNode("poolingEnabled")
     public boolean poolingEnabled = true;
 
+    protected LinkedHashSet<LdapEntry> ldapEntries;
+
+    protected boolean isDynamicServerList = false;
+
+    protected boolean useSsl = false;
+
+    protected final DNSServiceResolver srvResolver = DNSServiceResolverImpl.getInstance();
+
+    public boolean isDynamicServerList() {
+        return isDynamicServerList;
+    }
+
     public String getName() {
         return name;
     }
@@ -77,18 +112,193 @@
     }
 
     public String getLdapUrls() {
-        return ldapUrls;
+        if (ldapUrls != null) {
+            return ldapUrls;
+        }
+
+        // Leverage JNDI support for clustered servers by concatinating
+        // all the provided URLs for failover
+        StringBuilder calculatedLdapUrls = new StringBuilder();
+        for (LdapEntry entry : ldapEntries) {
+            calculatedLdapUrls.append(entry);
+            calculatedLdapUrls.append(' ');
+        }
+
+        /*
+         * If the configuration does not contain any domain entries then cache
+         * the urls, domain entries should always be re-queried however as the
+         * LDAP server list should change dynamically
+         */
+        if (!isDynamicServerList) {
+            return ldapUrls = calculatedLdapUrls.toString().trim();
+        }
+        return calculatedLdapUrls.toString().trim();
     }
 
-    @XNodeList(value = "ldapUrl", componentType = String.class, type = String[].class)
-    public void setLdapUrls(String[] ldapUrls) throws DirectoryException {
+    @XNodeList(value = "ldapUrl", componentType = LDAPUrlDescriptor.class, type = LDAPUrlDescriptor[].class)
+    public void setLdapUrls(LDAPUrlDescriptor[] ldapUrls)
+            throws DirectoryException {
         if (ldapUrls == null) {
             throw new DirectoryException(
                     "At least one <ldapUrl/> server declaration is required");
         }
-        // Leverage JNDI support for clustered servers by concatinating
-        // all the provided URLs for failover
-        this.ldapUrls = StringUtils.join(ldapUrls, " ");
+
+        ldapEntries = new LinkedHashSet<LdapEntry>();
+
+        Set<LDAPUrlDescriptor> processed = new HashSet<LDAPUrlDescriptor>();
+
+        List<String> urls = new ArrayList<String>(ldapUrls.length);
+        for (LDAPUrlDescriptor url : ldapUrls) {
+            LdapURL ldapUrl;
+            try {
+                /*
+                 * Empty string translates to ldap://localhost:389 through JNDI
+                 */
+                if (StringUtils.isEmpty(url.getValue())) {
+                    urls.add(url.getValue());
+                    ldapEntries.add(new LdapEntryDescriptor(url));
+                    continue;
+                }
+
+                /*
+                 * Parse the URI to make sure it is valid
+                 */
+                ldapUrl = new LdapURL(url.getValue());
+                if (!processed.add(url)) {
+                    continue;
+                }
+            } catch (NamingException e) {
+                throw new DirectoryException(e);
+            }
+
+            useSsl = useSsl || ldapUrl.useSsl();
+
+            /*
+             * RFC-2255 - The "ldap" prefix indicates an entry or entries
+             * residing in the LDAP server running on the given hostname at the
+             * given port number. The default LDAP port is TCP port 389. If no
+             * hostport is given, the client must have some apriori knowledge of
+             * an appropriate LDAP server to contact.
+             */
+            if (ldapUrl.getHost() == null) {
+                /*
+                 * RFC-2782 - Check to see if an LDAP SRV record is defined in
+                 * the DNS server
+                 */
+                String domain = convertDNtoFQDN(ldapUrl.getDN());
+                if (domain != null) {
+                    /*
+                     * Dynamic URL - retrieve from SRV record
+                     */
+                    List<String> discoveredUrls;
+                    try {
+                        discoveredUrls = discoverLdapServers(domain,
+                                ldapUrl.useSsl(), url.getSrvPrefix());
+                    } catch (NamingException e) {
+                        throw new DirectoryException(String.format(
+                                "SRV record DNS lookup failed for %s.%s: %s",
+                                url.getSrvPrefix(), domain, e.getMessage()), e);
+                    }
+
+                    /*
+                     * Discovered URLs could be empty, lets check at the end
+                     * though
+                     */
+                    urls.addAll(discoveredUrls);
+
+                    /*
+                     * Store entries in an ordered set and remember that we were
+                     * dynamic
+                     */
+                    ldapEntries.add(new LdapEntryDomain(url, domain,
+                            ldapUrl.useSsl()));
+                    isDynamicServerList = true;
+                } else {
+                    throw new DirectoryException(
+                            "Invalid LDAP SRV reference, this should be of the form"
+                                    + " ldap:///dc=example,dc=org");
+                }
+            } else {
+                /*
+                 * Static URL - store the value
+                 */
+                urls.add(url.getValue());
+
+                /*
+                 * Store entries in an ordered set
+                 */
+                ldapEntries.add(new LdapEntryDescriptor(url));
+            }
+        }
+
+        /*
+         * Oops no valid URLs to connect to :(
+         */
+        if (urls.isEmpty()) {
+            throw new DirectoryException(
+                    "No valid server urls returned from DNS query");
+        }
+    }
+
+    /**
+     * Whether this server descriptor defines a secure ldap connection
+     */
+    public boolean useSsl() {
+        return useSsl;
+    }
+
+    /**
+     * Retrieve server URLs from DNS SRV record
+     *
+     * @param domain The domain to query
+     * @param useSsl Whether the connection to this domain should be secure
+     * @return List of servers or empty list
+     * @throws NamingException if DNS lookup fails
+     */
+    protected List<String> discoverLdapServers(String domain, boolean useSsl,
+            String srvPrefix) throws NamingException {
+        List<String> result = new ArrayList<String>();
+        List<DNSServiceEntry> servers = getSRVResolver().resolveLDAPDomainServers(
+                domain, srvPrefix);
+
+        for (DNSServiceEntry serviceEntry : servers) {
+            /*
+             * Rebuild the URL
+             */
+            StringBuilder realUrl = (useSsl) ? new StringBuilder(LDAPS_SCHEME
+                    + "://") : new StringBuilder(LDAP_SCHEME + "://");
+            realUrl.append(serviceEntry);
+            result.add(realUrl.toString());
+        }
+        return result;
+    }
+
+    /**
+     * Convert domain from the ldap form dc=nuxeo,dc=org to the DNS domain name
+     * form nuxeo.org
+     *
+     * @param dn base DN of the domain
+     * @return the FQDN or null is DN is not matching the expected structure
+     * @throws DirectoryException is the DN is invalid
+     */
+    protected String convertDNtoFQDN(String dn) throws DirectoryException {
+        try {
+            LdapDN ldapDN = new LdapDN(dn);
+            Enumeration<String> components = ldapDN.getAll();
+            List<String> domainComponents = new ArrayList<String>();
+            while (components.hasMoreElements()) {
+                String component = components.nextElement();
+                if (component.startsWith("dc=")) {
+                    domainComponents.add(component.substring(3));
+                } else {
+                    break;
+                }
+            }
+            Collections.reverse(domainComponents);
+            return StringUtils.join(domainComponents.iterator(), ".");
+        } catch (InvalidNameException e) {
+            throw new DirectoryException(e);
+        }
     }
 
     public boolean isPoolingEnabled() {
@@ -102,4 +312,132 @@
     public void setConnectionTimeout(int connectionTimeout) {
         this.connectionTimeout = connectionTimeout;
     }
+
+    protected DNSServiceResolver getSRVResolver() {
+        return srvResolver;
+    }
+
+    /**
+     * Common internal interface for Ldap entries
+     *
+     * @author Bob Browning
+     */
+    protected interface LdapEntry {
+        public abstract String getUrl() throws NamingException;
+    }
+
+    /**
+     * Server URL implementation of {@link LdapEntry}
+     *
+     * @author Bob Browning
+     */
+    protected class LdapEntryDescriptor implements LdapEntry {
+        protected LDAPUrlDescriptor url;
+
+        public LdapEntryDescriptor(LDAPUrlDescriptor descriptor) {
+            this.url = descriptor;
+        }
+
+        @Override
+        public String toString() {
+            try {
+                return getUrl();
+            } catch (NamingException e) {
+                log.error(e, e);
+                return "[DNS lookup failed]";
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof LdapEntryDescriptor) {
+                return url.equals(obj);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return url.hashCode();
+        }
+
+        public String getUrl() throws NamingException {
+            return url.getValue();
+        }
+
+    }
+
+    /**
+     * Domain implementation of {@link LdapEntry} using DNS SRV record
+     *
+     * @author Bob Browning
+     */
+    protected final class LdapEntryDomain extends LdapEntryDescriptor {
+
+        protected final String domain;
+
+        protected final boolean useSsl;
+
+        public LdapEntryDomain(LDAPUrlDescriptor descriptor,
+                final String domain, boolean useSsl) {
+            super(descriptor);
+            this.domain = domain;
+            this.useSsl = useSsl;
+        }
+
+        @Override
+        public String getUrl() throws NamingException {
+            List<DNSServiceEntry> servers = getSRVResolver().resolveLDAPDomainServers(
+                    domain, url.getSrvPrefix());
+
+            StringBuilder result = new StringBuilder();
+            for (DNSServiceEntry serviceEntry : servers) {
+                /*
+                 * Rebuild the URL
+                 */
+                result.append(useSsl ? LDAPS_SCHEME + "://" : LDAP_SCHEME
+                        + "://");
+                result.append(serviceEntry);
+                result.append(' ');
+            }
+            return result.toString().trim();
+        }
+
+        private LDAPServerDescriptor getOuterType() {
+            return LDAPServerDescriptor.this;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + getOuterType().hashCode();
+            result = prime * result
+                    + ((domain == null) ? 0 : domain.hashCode());
+            result = prime * result + (useSsl ? 1231 : 1237);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (!super.equals(obj))
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            LdapEntryDomain other = (LdapEntryDomain) obj;
+            if (!getOuterType().equals(other.getOuterType()))
+                return false;
+            if (domain == null) {
+                if (other.domain != null)
+                    return false;
+            } else if (!domain.equals(other.domain))
+                return false;
+            if (useSsl != other.useSsl)
+                return false;
+            return true;
+        }
+    }
+
 }
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPSession.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPSession.java
--- a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPSession.java
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPSession.java
@@ -586,7 +586,7 @@
                     schemaName, id, fieldMap);
             return docModel;
         } catch (PropertyException e) {
-            log.error(e);
+            log.error(e, e);
             return null;
         }
     }
@@ -833,17 +833,30 @@
         Properties env = (Properties) directory.getContextProperties().clone();
         env.put(Context.SECURITY_PRINCIPAL, dn);
         env.put(Context.SECURITY_CREDENTIALS, password);
+
+        InitialDirContext authenticationDirContext = null;
         try {
             // creating a context does a bind
             log.debug(String.format("LDAP bind dn='%s'", dn));
             // noinspection ResultOfObjectAllocationIgnored
-            new InitialDirContext(env);
+            authenticationDirContext = new InitialDirContext(env);
             log.debug("Bind succeeded, authentication ok");
             return true;
         } catch (NamingException e) {
             log.debug("Bind failed: " + e.getMessage());
             // authentication failed
             return false;
+        } finally {
+            try {
+                if (authenticationDirContext != null) {
+                    authenticationDirContext.close();
+                }
+            } catch (NamingException e) {
+                log.error(
+                        "Error closing authentication context when biding dn "
+                                + dn, e);
+                return false;
+            }
         }
     }
 
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPTreeReference.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPTreeReference.java
--- a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPTreeReference.java
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPTreeReference.java
@@ -236,7 +236,6 @@
      * @see org.nuxeo.ecm.directory.Reference#getTargetIdsForSource(String)
      */
     // TODO: optimize reusing the same ldap session (see LdapReference optim method)
-    @SuppressWarnings("unchecked")
     public List<String> getTargetIdsForSource(String sourceId)
             throws DirectoryException {
         Set<String> targetIds = new TreeSet<String>();
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPUrlDescriptor.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPUrlDescriptor.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/LDAPUrlDescriptor.java
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Robert Browning - initial implementation
+ *     Nuxeo - code review and integration
+ */
+package org.nuxeo.ecm.directory.ldap;
+
+import org.nuxeo.common.xmap.annotation.XNode;
+import org.nuxeo.common.xmap.annotation.XObject;
+import org.nuxeo.ecm.directory.ldap.dns.DNSServiceResolver;
+
+@XObject
+public class LDAPUrlDescriptor {
+
+    @XNode(value = "@srvPrefix")
+    private String srvPrefix = DNSServiceResolver.LDAP_SERVICE_PREFIX;
+
+    @XNode
+    private String value;
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof LDAPUrlDescriptor) {
+            return value.equals(((LDAPUrlDescriptor) obj).value)
+                    && srvPrefix.equals(((LDAPUrlDescriptor) obj).srvPrefix);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return (srvPrefix + value).hashCode();
+    }
+
+    public String getSrvPrefix() {
+        return srvPrefix;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setSrvPrefix(String srvPrefix) {
+        this.srvPrefix = srvPrefix;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceEntry.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceEntry.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceEntry.java
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Robert Browning - initial implementation
+ *     Nuxeo - code review and integration
+ */
+package org.nuxeo.ecm.directory.ldap.dns;
+
+
+/**
+ * Encapsulates a hostname and port.
+ */
+public class DNSServiceEntry implements Comparable<DNSServiceEntry> {
+
+    private final String host;
+
+    private final int port;
+
+    private final int priority;
+
+    private final int weight;
+
+    public DNSServiceEntry(String host, int port, int priority, int weight) {
+        this.host = host;
+        this.port = port;
+        this.priority = priority;
+        this.weight = weight;
+    }
+
+    /**
+     * Get the priority of this DNS entry, descending priority
+     * 0(highest)..Integer.MAX_VALUE(lowest)
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Get the weight of this DNS entry to compare entries with equal
+     * priority, ascending weight 0(lowest)..Integer.MAX_VALUE(highest)
+     */
+    public int getWeight() {
+        return weight;
+    }
+
+    /**
+     * Returns the hostname.
+     */
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * Returns the port.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Return the hostname in the form (hostname:port)
+     */
+    @Override
+    public String toString() {
+        return host + ":" + port;
+    }
+
+    /**
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof DNSServiceEntry)) {
+            return false;
+        }
+
+        final DNSServiceEntry address = (DNSServiceEntry) o;
+
+        if (!host.equals(address.host)) {
+            return false;
+        }
+        return port == address.port;
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    /**
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public int compareTo(DNSServiceEntry o) {
+        if (o.priority == priority) {
+            return o.weight - weight;
+        }
+        return priority - o.priority;
+    }
+}
\ No newline at end of file
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolver.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolver.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolver.java
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Robert Browning - initial implementation
+ *     Nuxeo - code review and integration
+ */
+package org.nuxeo.ecm.directory.ldap.dns;
+
+import java.util.List;
+
+import javax.naming.NamingException;
+
+/**
+ * Utility to fetch SRV records from a DNS server to get the list of available
+ * ldap servers from the DN representation of the domain.
+ *
+ * http://en.wikipedia.org/wiki/SRV_record
+ *
+ * @author Robert Browning
+ */
+public interface DNSServiceResolver {
+
+    /**
+     * DNS Cache Expiry property
+     */
+    public static final String DNS_CACHE_EXPIRY = "org.nuxeo.ecm.directory.ldap.dns.cache.expiry";
+
+    /**
+     * Prefix to locate LDAP service on DNS Server<br/>
+     * <br/>
+     * <b>service</b>: _ldap<br/>
+     * <b>protocol</b>: _tcp
+     */
+    public static final String LDAP_SERVICE_PREFIX = "_ldap._tcp";
+
+    /**
+     * Returns a list of LDAP servers for the specified domain by performing an
+     * SRV DNS lookup on _ldap._tcp.${domain}
+     *
+     * @param domain
+     * @return the list of SRV dns entries
+     * @throws NamingException
+     */
+    public List<DNSServiceEntry> resolveLDAPDomainServers(final String domain)
+            throws NamingException;
+
+    /**
+     * Returns a list of LDAP servers for the specified domain by performing an
+     * SRV DNS lookup using a custom DNS service prefix.
+     *
+     * @param domain
+     * @param prefix custom SRV prefix such as "_gc._tcp"
+     * @return the list of SRV dns entries
+     * @throws NamingException
+     */
+    public List<DNSServiceEntry> resolveLDAPDomainServers(final String domain,
+            final String prefix) throws NamingException;
+
+}
\ No newline at end of file
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolverImpl.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolverImpl.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/dns/DNSServiceResolverImpl.java
@@ -0,0 +1,196 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Robert Browning - initial implementation
+ *     Nuxeo - code review and integration
+ */
+package org.nuxeo.ecm.directory.ldap.dns;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuxeo.runtime.api.Framework;
+
+/**
+ * Utility class to perform DNS lookups for services.
+ */
+public class DNSServiceResolverImpl implements DNSServiceResolver {
+
+    public static final Log log = LogFactory.getLog(DNSServiceResolverImpl.class);
+
+    protected static DNSServiceResolver instance;
+
+    protected static final String SRV_RECORD = "SRV";
+
+    /**
+     * Create a cache to hold the at most 100 recent DNS lookups for a period of
+     * 10 minutes.
+     */
+    protected Map<String, List<DNSServiceEntry>> cache = new HashMap<String, List<DNSServiceEntry>>();
+
+    protected long lastCacheUpdate = System.currentTimeMillis();
+
+    protected final long maxDelay;
+
+    protected static DirContext context;
+
+    public static final synchronized DNSServiceResolver getInstance() {
+        if (instance == null) {
+            instance = new DNSServiceResolverImpl();
+        }
+        return instance;
+    }
+
+    protected DNSServiceResolverImpl() {
+        /*
+         * The expiry of the cache in minutes
+         */
+        int cacheExpiry = 10;
+        try {
+            cacheExpiry = Integer.parseInt(Framework.getProperty(
+                    DNS_CACHE_EXPIRY, "10"));
+        } catch (NumberFormatException e) {
+            log.warn("invalid value for property: " + DNS_CACHE_EXPIRY
+                    + ", falling back to default value of 10 minutes");
+        }
+        maxDelay = 1000 * 60 * cacheExpiry;
+
+        Properties env = new Properties();
+        env.put("java.naming.factory.initial",
+                "com.sun.jndi.dns.DnsContextFactory");
+        try {
+            context = new InitialDirContext(env);
+        } catch (NamingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the host name and port that a server providing the specified
+     * service can be reached at. A DNS lookup for a SRV record in the form
+     * "_service.example.com" is attempted.
+     * <p>
+     * As an example, a lookup for "example.com" for the service _gc._tcp may
+     * return "dc01.example.com:3268".
+     *
+     * @param service the service.
+     * @param domain the domain.
+     * @return a List of DNSServiceEntrys, which encompasses the hostname and
+     *         port that the server can be reached at for the specified domain.
+     * @throws NamingException if the DNS server is unreachable
+     */
+    protected List<DNSServiceEntry> resolveDnsServiceRecord(
+            final String service, final String domain) throws NamingException {
+        List<DNSServiceEntry> addresses = new ArrayList<DNSServiceEntry>();
+
+        if (context == null) {
+            return addresses;
+        }
+
+        final String key = service + "." + domain;
+        /*
+         * Return item from cache if it exists.
+         */
+        if (System.currentTimeMillis() - lastCacheUpdate > maxDelay) {
+            cache.clear();
+        }
+        if (cache.containsKey(key)) {
+            addresses = cache.get(key);
+            if (addresses != null) {
+                return addresses;
+            }
+        }
+
+        Attributes dnsLookup = context.getAttributes(service + "." + domain,
+                new String[] { SRV_RECORD });
+
+        Attribute attribute = dnsLookup.get(SRV_RECORD);
+        for (int i = 0; i < attribute.size(); i++) {
+            /*
+             * Get the current resource record
+             */
+            String entry = (String) attribute.get(i);
+
+            String[] records = entry.split(" ");
+            String host = records[records.length - 1];
+            int port = Integer.parseInt(records[records.length - 2]);
+            int weight = Integer.parseInt(records[records.length - 3]);
+            int priority = Integer.parseInt(records[records.length - 4]);
+
+            /*
+             * possible to get TTL?
+             */
+
+            /*
+             * Host entries in DNS should end with a "."
+             */
+            if (host.endsWith(".")) {
+                host = host.substring(0, host.length() - 1);
+            }
+
+            addresses.add(new DNSServiceEntry(host, port, priority, weight));
+        }
+
+        /*
+         * Sort the addresses by DNS priority and weight settings
+         */
+        Collections.sort(addresses);
+
+        /*
+         * Add item to cache.
+         */
+        if (cache.size() > 100) {
+            cache.clear();
+        }
+        cache.put(key, addresses);
+        lastCacheUpdate = System.currentTimeMillis();
+        return addresses;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.nuxeo.ecm.directory.ldap.dns.DNSServiceResolver#resolveLDAPDomainServers
+     * (java.lang.String)
+     */
+    public List<DNSServiceEntry> resolveLDAPDomainServers(final String domain)
+            throws NamingException {
+        return resolveDnsServiceRecord(LDAP_SERVICE_PREFIX, domain);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.nuxeo.ecm.directory.ldap.dns.DNSServiceResolver#resolveLDAPDomainServers
+     * (java.lang.String, java.lang.String)
+     */
+    public List<DNSServiceEntry> resolveLDAPDomainServers(final String domain,
+            final String prefix) throws NamingException {
+        return resolveDnsServiceRecord(prefix, domain);
+    }
+}
\ No newline at end of file
diff --git a/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/filter/FilterExpressionCorrector.java b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/filter/FilterExpressionCorrector.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/main/java/org/nuxeo/ecm/directory/ldap/filter/FilterExpressionCorrector.java
@@ -0,0 +1,101 @@
+package org.nuxeo.ecm.directory.ldap.filter;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * </p>This class is used to apply corrections to a filter expression. It is
+ * mainly used for filters that come from an Active Directory.</p>
+ *
+ * <p>
+ * You can apply the corrections you want, in the desired order. The available
+ * jobs are listed in the enum FilterJobs.
+ * </p>
+ *
+ * <p>
+ * Example: <code>FilterExpressionCorrector.correctFilter(filterValue,
+                            FilterJobs.JOB1, FilterJobs.JOB2);</code>
+ * </p>
+ *
+ * @author Nicolas Ulrich <nulrich@nuxeo.com>
+ *
+ */
+public class FilterExpressionCorrector {
+
+    /**
+     * Enumeration of the available jobs
+     */
+    public enum FilterJobs {
+
+        CORRECT_NOT(notJob);
+
+        private final ICorrectorJob job;
+
+        FilterJobs(final ICorrectorJob job) {
+            this.job = job;
+        }
+
+        protected String run(final String filter) {
+            return job.run(filter);
+        }
+    }
+
+    /**
+     * A Job interface
+     */
+    interface ICorrectorJob {
+        String run(final String filter);
+    }
+
+    /**
+     * This job find "!expression" and replace it by "!(expression)"
+     */
+    private static ICorrectorJob notJob = new ICorrectorJob() {
+
+        public String run(final String filter) {
+
+            final StringBuffer newString = new StringBuffer();
+
+            // Find 'not' without parenthesis
+            final Pattern pattern = Pattern.compile("![^(][^\\)]+[)]");
+            final Matcher matcher = pattern.matcher(filter);
+
+            // Find all the matches.
+            while (matcher.find()) {
+
+                // Add parenthesis
+                final StringBuffer res = new StringBuffer(matcher.group());
+                res.insert(1, '(').append(')');
+
+                // Replace
+                matcher.appendReplacement(newString, res.toString());
+            }
+
+            matcher.appendTail(newString);
+
+            return newString.toString();
+
+        }
+
+    };
+
+    /**
+     * Apply the chosen Correctors to the filter expression
+     *
+     * @param filterExpresion The filter expression to correct
+     * @param jobs List of the jobs you want to apply.
+     * @return
+     */
+    public static String correctFilter(final String filterExpresion,
+            final FilterJobs... jobs) {
+
+        String result = filterExpresion;
+
+        for (FilterJobs job : jobs) {
+            result = job.run(result);
+        }
+
+        return result;
+    }
+
+}
diff --git a/nuxeo-platform-directory-ldap/src/main/resources/OSGI-INF/LDAPDirectoryFactory.xml b/nuxeo-platform-directory-ldap/src/main/resources/OSGI-INF/LDAPDirectoryFactory.xml
--- a/nuxeo-platform-directory-ldap/src/main/resources/OSGI-INF/LDAPDirectoryFactory.xml
+++ b/nuxeo-platform-directory-ldap/src/main/resources/OSGI-INF/LDAPDirectoryFactory.xml
@@ -31,6 +31,12 @@
           <ldapUrl>ldap://localhost:389</ldapUrl>
           <ldapUrl>ldap://server2:389</ldapUrl>
           <ldapUrl>ldaps://server3:389</ldapUrl>
+          
+          <!-- LDAP SRV DNS resolution on _ldap._tcp.example.com -->
+          <ldapUrl>ldap:///dc=example,dc=com</ldapUrl>
+          
+          <!-- LDAP SRV DNS resolution on _gc._tcp.example.com -->
+          <ldapUrl srvPrefix="_gc._tcp">ldap:///dc=example,dc=com</ldapUrl>
 
           <bindDn>cn=nuxeo5,ou=applications,dc=example,dc=com</bindDn>
           <bindPassword>changeme</bindPassword>
@@ -197,19 +203,42 @@
       single schema which is to be registered as any core document
       schema and that will be used to build a DocumentModel for each
       matching entry of the directory.
+      
+      
+      Nuxeo EP provides group resolution for statically dn-referenced
+      entries (in read and write mode) and for dynamically ldapUrl
+      matched entries (readonly).
 
       The references tags are used to dynamically build nxs:stringList
       fields of that schema that are to compute membership relationships
       between users and groups or between parent groups and sub groups.
       It can also resole children and parents following the ldap tree
       structure.
+      
+      You have two ways for declaring a dynamic reference in a 'references' tag.
+      You can use 'dynamicAttributeId' to point to an ldap attribute that 
+      contains an ldap url. 
+      
+      <code>
+      <ldapReference field="members" directory="userDirectory"
+               dynamicAttributeId="memberURL"/>
+      </code>
 
-      Nuxeo EP provides group resolution for statically dn-referenced
-      entries (in read and write mode) and for dynamically ldapUrl
-      matched entries (readonly).
+      Or, you can use a subtag 'dynamicReference' to declare attributes that 
+      contain a DN, a filter and a type. This values will be used to selected 
+      the references.
+
+      <code>
+      <ldapReference field="members" directory="userDirectory" />
+         <dynamicReference filter="ldapAttribute" 
+            type="subtree/onelevel" 
+            baseDN ="ldapAttribute" />
+      <ldapReference/>
+      </code>
 
       When using dynamic references, caching is advised since dynamic
       group resolution can be expensive.
+
     </documentation>
 
     <object
diff --git a/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/TestLDAPDescriptors.java b/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/TestLDAPDescriptors.java
--- a/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/TestLDAPDescriptors.java
+++ b/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/TestLDAPDescriptors.java
@@ -24,12 +24,11 @@
 
 import javax.naming.directory.SearchControls;
 
-import junit.framework.TestCase;
-
 import org.nuxeo.common.xmap.XMap;
 import org.nuxeo.ecm.directory.DirectoryException;
+import org.nuxeo.runtime.test.NXRuntimeTestCase;
 
-public class TestLDAPDescriptors extends TestCase {
+public class TestLDAPDescriptors extends NXRuntimeTestCase {
 
     protected LDAPDirectoryDescriptor directory;
 
@@ -43,7 +42,7 @@
     }
 
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
         XMap xmap = new XMap();
         xmap.register(LDAPServerDescriptor.class);
diff --git a/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/dns/LDAPServerDescriptorDNSTestCase.java b/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/dns/LDAPServerDescriptorDNSTestCase.java
new file mode 100644
--- /dev/null
+++ b/nuxeo-platform-directory-ldap/src/test/java/org/nuxeo/ecm/directory/ldap/dns/LDAPServerDescriptorDNSTestCase.java
@@ -0,0 +1,161 @@
+/*
+ * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Contributors:
+ *     Robert Browning - initial implementation
+ */
+package org.nuxeo.ecm.directory.ldap.dns;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuxeo.ecm.directory.ldap.LDAPServerDescriptor;
+import org.nuxeo.ecm.directory.ldap.LDAPUrlDescriptor;
+import org.nuxeo.runtime.test.NXRuntimeTestCase;
+
+import com.sun.jndi.ldap.LdapURL;
+
+/**
+ * Test case to ensure LDAPServerDescriptor correctly handles entries returned
+ * from DNSService implementation
+ *
+ * @author Bob Browning
+ */
+public class LDAPServerDescriptorDNSTestCase extends NXRuntimeTestCase {
+
+    private static final Log log = LogFactory.getLog(LDAPServerDescriptorDNSTestCase.class);
+
+    private final class MockDNSService implements DNSServiceResolver {
+
+        public List<DNSServiceEntry> resolveLDAPDomainServers(String domain,
+                String prefix) {
+            List<DNSServiceEntry> entries = new ArrayList<DNSServiceEntry>();
+            if (prefix.equals("_gc._tcp")) {
+                entries.add(new DNSServiceEntry("localhost", 3268, 0, 100));
+            } else if (prefix.equals("_ldap._tcp")) {
+                entries.add(new DNSServiceEntry("localhost", 389, 0, 100));
+            }
+            return entries;
+        }
+
+        public List<DNSServiceEntry> resolveLDAPDomainServers(String domain) {
+            List<DNSServiceEntry> entries = new ArrayList<DNSServiceEntry>();
+            entries.add(new DNSServiceEntry("localhost", 389, 0, 100));
+            return entries;
+        }
+
+    }
+
+    private final class MockLDAPServerDescriptor extends LDAPServerDescriptor {
+        /*
+         * Mock DNS Service
+         */
+        private final DNSServiceResolver service = new MockDNSService();
+
+        @Override
+        protected DNSServiceResolver getSRVResolver() {
+            return service;
+        };
+
+    }
+
+    private MockDNSService dns;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        dns = new MockDNSService();
+    }
+
+    private final String domain = "ldap:///dc=nuxeo,dc=org";
+
+    /**
+     * Unit test the set/get LdapUrl methods
+     *
+     * @throws Exception
+     */
+    public void testLdapServerDnsParsing() throws Exception {
+        List<DNSServiceEntry> actual = dns.resolveLDAPDomainServers("nuxeo.org");
+        if (log.isDebugEnabled()) {
+            log.debug(actual);
+        }
+
+        /*
+         * Convert our discovered server list into URIs
+         */
+        LDAPUrlDescriptor[] uris = new LDAPUrlDescriptor[actual.size()];
+        int i = 0;
+        for (DNSServiceEntry serviceEntry : actual) {
+            LDAPUrlDescriptor u = new LDAPUrlDescriptor();
+            u.setValue("ldap://" + serviceEntry.toString());
+            uris[i++] = u;
+        }
+
+        LDAPServerDescriptor d = new MockLDAPServerDescriptor();
+
+        d.setLdapUrls(uris);
+        String testA = d.getLdapUrls();
+        if (log.isDebugEnabled()) {
+            log.debug(testA);
+        }
+        assertEquals("ldap://localhost:389", testA);
+
+        d = new MockLDAPServerDescriptor();
+
+        /*
+         * _ldap._tcp test
+         */
+        LDAPUrlDescriptor u = new LDAPUrlDescriptor();
+        u.setValue(domain);
+
+        /*
+         * _gc._tcp test
+         */
+        LDAPUrlDescriptor u2 = new LDAPUrlDescriptor();
+        u2.setSrvPrefix("_gc._tcp");
+        u2.setValue(domain);
+
+        d.setLdapUrls(new LDAPUrlDescriptor[] { u, u2 });
+
+        for (int j = 0; j < 100; j++) {
+            String urls = d.getLdapUrls();
+            if (log.isDebugEnabled()) {
+                log.debug(urls);
+            }
+            assertEquals("ldap://localhost:389 ldap://localhost:3268", urls);
+        }
+
+        d.setLdapUrls(new LDAPUrlDescriptor[] { u });
+
+        /*
+         * Assert that when run using empty hostname in URI we get the same
+         * result as passing in the list of servers
+         */
+        assertEquals(testA, d.getLdapUrls());
+    }
+
+    /**
+     * Ensure LdapURL correctly determines SSL support of server URL
+     *
+     * @throws Exception
+     */
+    public void testLdapUrlSslSupport() throws Exception {
+        LdapURL url = new LdapURL("ldap:///dc=example,dc=com");
+        assertFalse(url.useSsl());
+
+        url = new LdapURL("ldaps:///dc=example,dc=com");
+        assertTrue(url.useSsl());
+    }
+}
diff --git a/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAP.xml b/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAP.xml
--- a/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAP.xml
+++ b/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAP.xml
@@ -17,11 +17,11 @@
       <ldapUrl/> tag for each replica of the cluster.
     -->
     <server name="default">
-
-      <ldapUrl>ldap://localhost:389</ldapUrl>
+      <ldapUrl>ldap://localhost:1389</ldapUrl>
       <!-- Optional servers from the same cluster
         <ldapUrl>ldap://server2:389</ldapUrl>
         <ldapUrl>ldap://server3:389</ldapUrl>
+        <ldapUrl srvPrefix="_ldaptest._tcp">ldap:///dc = in, DC= nuxeo,dc=com</ldapUrl>
       -->
 
       <!-- Credentials used by Nuxeo5 to browse the directory, create
diff --git a/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAPWithBBB.xml b/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAPWithBBB.xml
--- a/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAPWithBBB.xml
+++ b/nuxeo-platform-directory-ldap/src/test/resources/TestDirectoriesWithExternalOpenLDAPWithBBB.xml
@@ -16,7 +16,7 @@
     -->
     <server name="default">
 
-      <ldapUrl>ldap://localhost:389</ldapUrl>
+      <ldapUrl>ldap://localhost:1389</ldapUrl>
       <!-- Optional servers from the same cluster
         <ldapUrl>ldap://server2:389</ldapUrl>
         <ldapUrl>ldap://server3:389</ldapUrl>
