/* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive; import org.ldaptive.control.RequestControl; import org.ldaptive.provider.Provider; import org.ldaptive.provider.ProviderConnection; import org.ldaptive.provider.ProviderConnectionFactory; import org.ldaptive.provider.jndi.JndiProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creates connections for performing ldap operations. * * @author Middleware Services */ public class DefaultConnectionFactory implements ConnectionFactory { /** Ldap provider class name. */ public static final String PROVIDER = "org.ldaptive.provider"; /** Static reference to the default ldap provider. */ protected static final Provider<?> DEFAULT_PROVIDER = getDefaultProvider(); /** Provider used by this factory. */ private Provider<?> provider = DEFAULT_PROVIDER.newInstance(); /** Connection configuration used by this factory. */ private ConnectionConfig config; /** Default constructor. */ public DefaultConnectionFactory() {} /** * Creates a new default connection factory. * * @param ldapUrl to connect to */ public DefaultConnectionFactory(final String ldapUrl) { setConnectionConfig(new ConnectionConfig(ldapUrl)); } /** * Creates a new default connection factory. * * @param cc connection configuration */ public DefaultConnectionFactory(final ConnectionConfig cc) { setConnectionConfig(cc); } /** * Creates a new default connection factory. * * @param cc connection configuration * @param p provider */ public DefaultConnectionFactory(final ConnectionConfig cc, final Provider<?> p) { setConnectionConfig(cc); setProvider(p); } /** * Returns the connection config. * * @return connection config */ public ConnectionConfig getConnectionConfig() { return config; } /** * Sets the connection config. Once invoked the supplied connection config is made immutable. See {@link * ConnectionConfig#makeImmutable()}. * * @param cc connection config */ public void setConnectionConfig(final ConnectionConfig cc) { config = cc; config.makeImmutable(); } /** * Returns the ldap provider. * * @return ldap provider */ public Provider<?> getProvider() { return provider; } /** * Sets the ldap provider. * * @param p ldap provider to set */ public void setProvider(final Provider<?> p) { provider = p; } /** * Creates a new connection. Connections returned from this method must be opened before they can perform ldap * operations. * * @return connection */ @Override public Connection getConnection() { return new DefaultConnection(config, provider.getConnectionFactory(config)); } /** * Creates a new connection. Connections returned from this method must be opened before they can be used. * * @param ldapUrl to connect to * * @return connection */ public static Connection getConnection(final String ldapUrl) { final Provider<?> p = DEFAULT_PROVIDER.newInstance(); final ConnectionConfig cc = new ConnectionConfig(ldapUrl); cc.makeImmutable(); return new DefaultConnection(cc, p.getConnectionFactory(cc)); } /** * Creates a new connection. Connections returned from this method must be opened before they can be used. * * @param cc connection configuration * * @return connection */ public static Connection getConnection(final ConnectionConfig cc) { final Provider<?> p = DEFAULT_PROVIDER.newInstance(); cc.makeImmutable(); return new DefaultConnection(cc, p.getConnectionFactory(cc)); } /** * The {@link #PROVIDER} property is checked and that class is loaded if provided. Otherwise the JNDI provider is * returned. * * @return default provider */ public static Provider<?> getDefaultProvider() { Provider<?> p; final String providerClass = System.getProperty(PROVIDER); if (providerClass != null) { final Logger l = LoggerFactory.getLogger(DefaultConnectionFactory.class); try { l.info("Setting ldap provider to {}", providerClass); p = (Provider<?>) Class.forName(providerClass).newInstance(); } catch (Exception e) { l.error("Error instantiating {}", providerClass, e); throw new IllegalStateException(e); } } else { // set the default ldap provider to JNDI p = new JndiProvider(); } return p; } @Override public String toString() { return String.format("[%s@%d::provider=%s, config=%s]", getClass().getName(), hashCode(), provider, config); } /** Default implementation for managing a connection to an LDAP. */ protected static class DefaultConnection implements Connection { /** Logger for this class. */ protected final Logger logger = LoggerFactory.getLogger(getClass()); /** Connection configuration. */ private final ConnectionConfig config; /** Connection factory. */ private final ProviderConnectionFactory<?> providerConnectionFactory; /** Provider connection. */ private ProviderConnection providerConnection; /** * Creates a new default connection. * * @param cc connection configuration * @param cf provider connection factory */ public DefaultConnection(final ConnectionConfig cc, final ProviderConnectionFactory<?> cf) { config = cc; providerConnectionFactory = cf; } @Override public ConnectionConfig getConnectionConfig() { return config; } /** * Returns the provider specific connection. Must be called after a successful call to {@link #open()}. * * @return provider connection * * @throws IllegalStateException if the connection is not open */ @Override public ProviderConnection getProviderConnection() { if (!isOpen()) { throw new IllegalStateException("Connection is not open"); } return providerConnection; } /** * This will establish a connection if one does not already exist. This connection should be closed using {@link * #close()}. * * @return response associated with the {@link ConnectionInitializer} or an empty response if no connection * initializer was configured * * @throws IllegalStateException if the connection is already open * @throws LdapException if the LDAP cannot be reached */ @Override public synchronized Response<Void> open() throws LdapException { if (isOpen()) { throw new IllegalStateException("Connection already open"); } providerConnection = providerConnectionFactory.create(); if (config.getConnectionInitializer() != null) { return config.getConnectionInitializer().initialize(this); } else { return new Response<>(null, null); } } /** * This will establish a connection if one does not already exist and bind to the LDAP using the supplied bind * request. This connection should be closed using {@link #close()}. * * @param request bind request * * @return response associated with the bind operation * * @throws IllegalStateException if the connection is already open * @throws LdapException if the LDAP cannot be reached */ @Override public synchronized Response<Void> open(final BindRequest request) throws LdapException { if (isOpen()) { throw new IllegalStateException("Connection already open"); } providerConnection = providerConnectionFactory.create(); return providerConnection.bind(request); } /** * Returns whether the underlying provider connection is not null. * * @return whether the provider connection has been initialized */ @Override public boolean isOpen() { return providerConnection != null; } @Override public synchronized void close() { try { if (isOpen()) { providerConnection.close(null); } } catch (LdapException e) { logger.warn("Error closing connection with the LDAP", e); } finally { providerConnection = null; } } @Override public synchronized void close(final RequestControl[] controls) { try { if (isOpen()) { providerConnection.close(controls); } } catch (LdapException e) { logger.warn("Error closing connection with the LDAP", e); } finally { providerConnection = null; } } @Override public synchronized Response<Void> reopen() throws LdapException { try { if (providerConnection != null) { providerConnection.close(null); } } catch (LdapException e) { logger.warn("Error closing connection with the LDAP", e); } finally { providerConnection = null; } providerConnection = providerConnectionFactory.create(); if (config.getConnectionInitializer() != null) { return config.getConnectionInitializer().initialize(this); } else { return new Response<>(null, null); } } @Override public synchronized Response<Void> reopen(final BindRequest request) throws LdapException { try { if (providerConnection != null) { providerConnection.close(null); } } catch (LdapException e) { logger.warn("Error closing connection with the LDAP", e); } finally { providerConnection = null; } providerConnection = providerConnectionFactory.create(); return providerConnection.bind(request); } @Override public String toString() { return String.format( "[%s@%d::config=%s, providerConnectionFactory=%s, " + "providerConnection=%s]", getClass().getName(), hashCode(), config, providerConnectionFactory, providerConnection); } @Override protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } } }