== Review == === LDAP Import Null Pointer Exception Log Snippet === * Portal LDAP import process reports the following stack trace when importing user "WESLEY WRIGHT" ** NOTE: java.net.SocketTimeoutException is only visible with DEBUG logging is enabled for class PortalLDAPUtil {code} 2016-10-28 00:16:25,070 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][LDAPSettingsUtil:?] -- listing properties --_lastName=sn_screenName=sAMAccountName_firstName=givenName_emailAddress=mail_group=memberOf_fullName=displayName_ [Sanitized] 2016-10-28 00:16:25,071 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][LDAPSettingsUtil:?] -- listing properties --_location=physicalDeliveryOfficeName_staff-code=extensionAttribute15_upn=userPrincipalName_ [Sanitized] 2016-10-28 00:16:25,071 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][LDAPSettingsUtil:?] -- listing properties --_ [Sanitized] 2016-10-28 00:16:25,071 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][LDAPSettingsUtil:?] -- listing properties --_ [Sanitized] 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute sAMAccountName: ses51472 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute mail: WESLEY.WRIGHT@members.ses.vic.gov.au 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute displayName: WESLEY WRIGHT 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute userPrincipalName: ses51472@members.ses.vic.gov.au 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute physicalDeliveryOfficeName: MALLACOOTA 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute memberOf: CN=SES_Volunteers,OU=User Groups,OU=Business Roles,OU=Groups,DC=vicses,DC=ses,DC=vic,DC=gov,DC=au, CN=IMS Practice,OU=Practice,OU=OIMS,OU=Groups,DC=vicses,DC=ses,DC=vic,DC=gov,DC=au, CN=IRS Practice,OU=Practice,OU=OIMS,OU=Groups,DC=vicses,DC=ses,DC=vic,DC=gov,DC=au, CN=TRAIN Practice,OU=Practice,OU=OIMS,OU=Groups,DC=vicses,DC=ses,DC=vic,DC=gov,DC=au 2016-10-28 00:16:25,087 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute givenName: WESLEY 2016-10-28 00:16:25,088 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute sn: WRIGHT 2016-10-28 00:16:25,088 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] LDAP user attribute extensionAttribute15: V 2016-10-28 00:16:25,088 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][DefaultLDAPToPortalConverter:?] Screen name ses51472 and email address WESLEY.WRIGHT@members.ses.vic.gov.au 2016-10-28 00:16:25,281 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPImporterImpl:?] User wesley.wright@members.ses.vic.gov.au is already synchronized, but updated password to avoid a blank value 2016-10-28 00:16:25,308 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] {java.naming.provider.url=ldaps://empoint-dc.vicses-prod.internal, java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, com.sun.jndi.ldap.connect.timeout=500, com.sun.jndi.ldap.connect.pool=true, java.naming.security.principal=CN=Service Account - EMPoint,OU=Generic,OU=Service Accounts,DC=vicses,DC=ses,DC=vic,DC=gov,DC=au, java.naming.security.credentials=********, java.naming.referral=follow, com.sun.jndi.ldap.read.timeout=15000} 2016-10-28 00:16:25,812 ip-10-54-1-223 WARN [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] Failed to bind to the LDAP server 2016-10-28 00:16:25,812 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][PortalLDAPUtil:?] javax.naming.CommunicationException: empoint-dc.vicses-prod.internal:636 [Root exception is java.net.SocketTimeoutException: connect timed out] javax.naming.CommunicationException: empoint-dc.vicses-prod.internal:636 [Root exception is java.net.SocketTimeoutException: connect timed out] at com.sun.jndi.ldap.Connection.(Connection.java:216) at com.sun.jndi.ldap.LdapClient.(LdapClient.java:137) at com.sun.jndi.ldap.LdapClient.getInstance(LdapClient.java:1614) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2746) at com.sun.jndi.ldap.LdapCtx.(LdapCtx.java:319) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:192) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:210) at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:153) at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:83) at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313) at javax.naming.InitialContext.init(InitialContext.java:244) at javax.naming.ldap.InitialLdapContext.(InitialLdapContext.java:154) at com.liferay.portal.security.ldap.PortalLDAPUtil.getContext(PortalLDAPUtil.java:116) at com.liferay.portal.security.ldap.PortalLDAPUtil.getContext(PortalLDAPUtil.java:81) at com.liferay.portal.security.ldap.PortalLDAPUtil.getUser(PortalLDAPUtil.java:466) at com.liferay.portal.security.ldap.PortalLDAPUtil.getUser(PortalLDAPUtil.java:455) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importGroups(PortalLDAPImporterImpl.java:813) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAPByUser(PortalLDAPImporterImpl.java:716) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAP(PortalLDAPImporterImpl.java:204) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAP(PortalLDAPImporterImpl.java:140) at com.liferay.portal.security.ldap.PortalLDAPImporterUtil.importFromLDAP(PortalLDAPImporterUtil.java:43) at com.liferay.portlet.admin.messaging.LDAPImportMessageListener.doImportOnStartup(LDAPImportMessageListener.java:38) at com.liferay.portlet.admin.messaging.LDAPImportMessageListener.doReceive(LDAPImportMessageListener.java:48) at com.liferay.portal.kernel.messaging.BaseMessageListener.receive(BaseMessageListener.java:26) at sun.reflect.GeneratedMethodAccessor728.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.liferay.portal.kernel.bean.ClassLoaderBeanHandler.invoke(ClassLoaderBeanHandler.java:67) at com.sun.proxy.$Proxy308.receive(Unknown Source) at com.liferay.portal.kernel.scheduler.messaging.SchedulerEventMessageListenerWrapper.receive(SchedulerEventMessageListenerWrapper.java:77) at com.liferay.portal.kernel.messaging.InvokerMessageListener.receive(InvokerMessageListener.java:72) at com.liferay.portal.kernel.messaging.ParallelDestination$1.run(ParallelDestination.java:71) at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask._runTask(ThreadPoolExecutor.java:682) at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask.run(ThreadPoolExecutor.java:593) at java.lang.Thread.run(Thread.java:745) Caused by: java.net.SocketTimeoutException: connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668) at sun.reflect.GeneratedMethodAccessor3560.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.sun.jndi.ldap.Connection.createSocket(Connection.java:311) at com.sun.jndi.ldap.Connection.(Connection.java:203) ... 35 more 2016-10-28 00:16:25,813 ip-10-54-1-223 ERROR [liferay/scheduler_dispatch-2][PortalLDAPImporterImpl:?] Unable to import user CN=WESLEY WRIGHT,OU=August,OU=Housekeeping: null:null:{samaccountname=sAMAccountName: ses51472} java.lang.NullPointerException at com.liferay.portal.security.ldap.PortalLDAPUtil.getNameInNamespace(PortalLDAPUtil.java:434) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importGroups(PortalLDAPImporterImpl.java:817) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAPByUser(PortalLDAPImporterImpl.java:716) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAP(PortalLDAPImporterImpl.java:204) at com.liferay.portal.security.ldap.PortalLDAPImporterImpl.importFromLDAP(PortalLDAPImporterImpl.java:140) at com.liferay.portal.security.ldap.PortalLDAPImporterUtil.importFromLDAP(PortalLDAPImporterUtil.java:43) at com.liferay.portlet.admin.messaging.LDAPImportMessageListener.doImportOnStartup(LDAPImportMessageListener.java:38) at com.liferay.portlet.admin.messaging.LDAPImportMessageListener.doReceive(LDAPImportMessageListener.java:48) at com.liferay.portal.kernel.messaging.BaseMessageListener.receive(BaseMessageListener.java:26) at sun.reflect.GeneratedMethodAccessor728.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.liferay.portal.kernel.bean.ClassLoaderBeanHandler.invoke(ClassLoaderBeanHandler.java:67) at com.sun.proxy.$Proxy308.receive(Unknown Source) at com.liferay.portal.kernel.scheduler.messaging.SchedulerEventMessageListenerWrapper.receive(SchedulerEventMessageListenerWrapper.java:77) at com.liferay.portal.kernel.messaging.InvokerMessageListener.receive(InvokerMessageListener.java:72) at com.liferay.portal.kernel.messaging.ParallelDestination$1.run(ParallelDestination.java:71) at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask._runTask(ThreadPoolExecutor.java:682) at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask.run(ThreadPoolExecutor.java:593) at java.lang.Thread.run(Thread.java:745) 2016-10-28 00:16:25,813 ip-10-54-1-223 DEBUG [liferay/scheduler_dispatch-2][LDAPSettingsUtil:?] -- listing properties --_lastName=sn_screenName=sAMAccountName_firstName=givenName_emailAddress=mail_group=memberOf_fullName=displayName_ [Sanitized] {code} === LDAP Import Null Pointer Exception Walkthrough === * PortalLDAPUtil tries to open an LDAP connection in method getContext on line 116. * LDAP connection fails due to LDAP server socket timeout and exception is caught in catch block * Exception is only logged when DEBUG logging is enabled. A WARN message is logged without any context. {code} class com.liferay.portal.security.ldap;.PortalLDAPUtil public static LdapContext getContext( long companyId, String providerURL, String principal, String credentials) throws Exception { // . . . LdapContext ldapContext = null; try { // line 116 ldapContext = new InitialLdapContext(environmentProperties, null); } // lines 118 to 126 catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn("Failed to bind to the LDAP server"); } if (_log.isDebugEnabled()) { _log.debug(e, e); } } return ldapContext; } {code} * Method getContext in PortalLDAPUtil returns a NULL value for LdapContext. * NULL value for LdapContext is returned on line 813 to method importGroups in class PortalLDAPImporterImpl {code} class com.liferay.portal.security.ldap.PortalLDAPImporterImpl; protected void importGroups( long ldapServerId, long companyId, LdapContext ldapContext, Attributes attributes, User user, Properties userMappings, Properties groupMappings) throws Exception { // ... // line 813 Binding binding = PortalLDAPUtil.getUser( ldapServerId, companyId, user.getScreenName(), user.getEmailAddress()); // line 817 String fullUserDN = PortalLDAPUtil.getNameInNamespace( ldapServerId, companyId, binding); // ... {code} * NULL value for local variable binding is passed on line 817 to method getNameInNamespace in class PortalLDAPUtil. * NULL value for parameter variable binding is passed on line 817 to method getNameInNamespace in class PortalLDAPUtil. * NULL value for parameter variable binding triggers a Null Pointer Exception (NPE) on line 434. {code} class com.liferay.portal.security.ldap.PortalLDAPUtil public static String getNameInNamespace( long ldapServerId, long companyId, Binding binding) throws Exception { // ... // line 434 String name = binding.getName(); // ... } {code} == Recommendations == * Log exceptions in method getContext of class PortalLDAPUtil. ** NOTE: Logging exceptions will help portal administrators and/or developer to find and resolve errors quickly. * Wrap exception and/or propogate source exceptions from method getContext in class PortalLDAPUtil to caller. ** NOTE: Wrapping exception and/or propogate source exceptions will trigger LDAP import to fail early. e.g. {code} class com.liferay.portal.security.ldap;.PortalLDAPUtil public static LdapContext getContext( long companyId, String providerURL, String principal, String credentials) throws Exception { // . . . LdapContext ldapContext = null; try { // line 116 ldapContext = new InitialLdapContext(environmentProperties, null); } // lines 118 to 126 catch (Exception e) { String msg = "Failed to bind to the LDAP server : " + e.getMessage(); _log.error(msg,e); throw e; } return ldapContext; } {code} * NOTE: Ignoring and/or hiding exceptions makes it very difficult to find the root cause of LDAP connection and access issues, such as LDAP server socket timeouts and results in LDAP import code failing unexpectedly later.