Tomcat 8 - LDAP: NameNotFoundException код ошибки 32, оставшееся имя пустая строка

Попытка перенести приложение из WebLogic 12.2.1 в Tomcat 8.5.4, что под Weblogic было введено, поскольку внешние поставщики JNDI для подключения LDAP были перенесены к новому Resource под Tomcat.

Следуя этим советам в Stack Overflow, пользовательский LdapContextFactory был упакован как новый jar файл в папке Tomcat lib.

В файле Tomcat server.xml был настроен следующий GlobalNamingResources/Resource:

    <Resource name="ldapConnection" 
        auth="Container"
        type="javax.naming.ldap.LdapContext"
        factory="com.sample.custom.LdapContextFactory"
        singleton="false"
        java.naming.referral="follow"
        java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
        java.naming.provider.url="ldap://some.host:389"
        java.naming.security.authentication="simple"
        java.naming.security.principal="CN=some,OU=some,OU=some,DC=some,DC=a,DC=b"
        java.naming.security.credentials="password"
        com.sun.jndi.ldap.connect.pool="true"
        com.sun.jndi.ldap.connect.pool.maxsize="10"
        com.sun.jndi.ldap.connect.pool.prefsize="4"
        com.sun.jndi.ldap.connect.pool.timeout="30000" />

Соединение выше отлично работает при просмотре каталога LDAP через браузер LDAP, например Apache Directory Studio/LDAP Browser, встроенный в Eclipse.

Пользовательский com.sample.custom.LdapContextFactory довольно прост:

public class LdapContextFactory implements ObjectFactory {

    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {

        Hashtable<Object, Object> env = new Hashtable<>();
        Reference reference = (Reference) obj;
        Enumeration<RefAddr> references = reference.getAll();

        while (references.hasMoreElements()) {
            RefAddr address = references.nextElement();
            String type = address.getType();
            String content = (String) address.getContent();
            env.put(type, content);
        }
        return new InitialLdapContext(env, null);
    }
}

Однако при запуске Tomcat бросает следующее исключение:

07-Sep-2016 15:04:01.064 SEVERE [main] org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans Exception processing Global JNDI Resources
 javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of:
    ''
 ]; remaining name ''
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888)
    at com.sun.jndi.ldap.LdapCtx.c_listBindings(LdapCtx.java:1189)
    at com.sun.jndi.toolkit.ctx.ComponentContext.p_listBindings(ComponentContext.java:592)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:330)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:317)
    at javax.naming.InitialContext.listBindings(InitialContext.java:472)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:136)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:145)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:110)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.lifecycleEvent(GlobalResourcesLifecycleListener.java:82)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:94)
    at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:401)
    at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:345)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:784)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:655)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:355)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:495)

Подобные вопросы и исследования предполагают недопустимое DN LDAP, но:

  • Такая же конфигурация LDAP отлично работает с клиентом LDAP
  • Поиск не выполняется, во время запуска Tomcat генерирует это исключение без запроса
  • Ошибка указывает на пустую строку '' как remaining name, поэтому на самом деле ничего не найдено, видимо

Вопрос (ы). Является ли это правильным способом переноса внешней записи JNDI-провайдеров из WebLogic в Tomcat? Как исправить недопустимую запись DN LDAP с пустым оставшимся именем? Может ли отсутствовать baseDN для настройки где-нибудь?


Обновление
Точная ошибка возникает при изменении LdapContextFactory на следующие, как это предлагается через комментарии:

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
        throws Exception {

    Hashtable<Object, Object> env = new Hashtable<>();
    Reference reference = (Reference) obj;
    Enumeration<RefAddr> references = reference.getAll();

    String providerUrl = "no valid URL";

    while (references.hasMoreElements()) {
        RefAddr address = references.nextElement();
        String type = address.getType();
        String content = (String) address.getContent();

        switch (type) {
        case Context.PROVIDER_URL:
            env.put(Context.PROVIDER_URL, content);
            providerUrl = content;
            break;

        default:
            env.put(type, content);
            break;
        }
    }

    InitialLdapContext context = null;
    Object result = null;
    try {
        context = new InitialLdapContext(env, null);

        LOGGER.info("looking up for " + providerUrl);
        result = context.lookup(providerUrl);
    } finally {
        if (context != null) {
            context.close();
        }
    }
    LOGGER.info("Created new LDAP Context");
    return result;
}

Изменение подтверждается путем ведения журнала, чтобы убедиться, что он был развернут правильно.

Входящий слушатель определяется по умолчанию в верхней части файла server.xml как

<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

И не может быть отключен согласно официальная документация:

Слушатель жизненного цикла Global Resources инициализирует глобальные ресурсы JNDI, определенные в server.xml, как часть элемента Global Resources. Без этого слушателя ни один из глобальных ресурсов не будет доступен.


То же самое происходит и с версией Tomcat 8.5.5 и 7.0.69: просто добавьте новый глобальный ресурс, как указано выше, и дополнительную банку, предоставляющую factory выше, будет выбрано исключение, указывающее на пустое оставшееся имя.

Ответ 1

Утилита stacktrace ушла, добавив к свойству java.naming.provider.url DN схемы LDAP, используя первую реализацию factory, предоставленную в вопросе.

Ниже скриншота клиента LDAP, используемого в этом контексте, Apache Directory Studio/LDAP Browser, встроенного в Eclipse, из которого можно было просмотреть соответствующий LDAP, просто используя начальные значения вопроса.

введите описание изображения здесь

Добавив DN схемы элемента Root к URL-адресу соединения, исключение исчезло, и ресурс LDAP теперь используется совместно с JNDI в Tomcat 8.


Подробнее о результатах поиска неисправностей:

В Tomcat 8 глобальные ресурсы обрабатываются через глобальный приемник ресурсов, GlobalResourcesLifecycleListener, определенный по умолчанию в файле server.xml. Такой слушатель вызывает a context.listBindings("") в bean создании, следовательно, эффективно просматривает каталог LDAP.

Этот первоначальный просмотр может, вероятно, быть разницей между Tomcat и WebLogic, где LDAP просматривается через JNDI только тогда, когда это требуется, следовательно, с помощью прямого запроса, а не при запуске с общим запросом. Таким образом, в Tomcat URL-адрес LDAP потребует дополнительной информации, то есть немного другой конфигурации как части его URL-адреса, чтобы напрямую указывать на действительное базовое DN.

От официального Документация WebLogic:

При запуске WebLogic Server пытается подключиться к источнику JNDI. Если соединение выполнено успешно, WebLogic Server настраивает запрошенные объекты и ссылки в локальном дереве JNDI, делая их доступными для клиентов WebLogic Server.

Следовательно, соединение довольно проще, чем listBindings:

Перечисляет имена, связанные в указанном контексте, а также связанные с ними объекты. Содержимое любых подконтексов не включается.