com.unboundid.ldap.sdk.RoundRobinDNSServerSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unboundid-ldapsdk Show documentation
Show all versions of unboundid-ldapsdk Show documentation
The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use
Java API for communicating with LDAP directory servers and performing
related tasks like reading and writing LDIF, encoding and decoding data
using base64 and ASN.1 BER, and performing secure communication. This
package contains the Standard Edition of the LDAP SDK, which is a
complete, general-purpose library for communicating with LDAPv3 directory
servers.
/*
* Copyright 2014-2022 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2014-2022 Ping Identity Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2014-2022 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.ldap.sdk;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.net.SocketFactory;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.ObjectPair;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadLocalRandom;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;
import static com.unboundid.ldap.sdk.LDAPMessages.*;
/**
* This class provides a server set implementation that handles the case in
* which a given host name may resolve to multiple IP addresses. Note that
* while a setup like this is typically referred to as "round-robin DNS", this
* server set implementation does not strictly require DNS (as names may be
* resolved through alternate mechanisms like a hosts file or an alternate name
* service), and it does not strictly require round-robin use of those addresses
* (as alternate ordering mechanisms, like randomized or failover, may be used).
*
* Example
* The following example demonstrates the process for creating a round-robin DNS
* server set for the case in which the hostname "directory.example.com" may be
* associated with multiple IP addresses, and the LDAP SDK should attempt to use
* them in a round robin manner.
*
* // Define a number of variables that will be used by the server set.
* String hostname = "directory.example.com";
* int port = 389;
* AddressSelectionMode selectionMode =
* AddressSelectionMode.ROUND_ROBIN;
* long cacheTimeoutMillis = 3600000L; // 1 hour
* String providerURL = "dns:"; // Default DNS config.
* SocketFactory socketFactory = null; // Default socket factory.
* LDAPConnectionOptions connectionOptions = null; // Default options.
*
* // Create the server set using the settings defined above.
* RoundRobinDNSServerSet serverSet = new RoundRobinDNSServerSet(hostname,
* port, selectionMode, cacheTimeoutMillis, providerURL, socketFactory,
* connectionOptions);
*
* // Verify that we can establish a single connection using the server set.
* LDAPConnection connection = serverSet.getConnection();
* RootDSE rootDSEFromConnection = connection.getRootDSE();
* connection.close();
*
* // Verify that we can establish a connection pool using the server set.
* SimpleBindRequest bindRequest =
* new SimpleBindRequest("uid=pool.user,dc=example,dc=com", "password");
* LDAPConnectionPool pool =
* new LDAPConnectionPool(serverSet, bindRequest, 10);
* RootDSE rootDSEFromPool = pool.getRootDSE();
* pool.close();
*
*/
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class RoundRobinDNSServerSet
extends ServerSet
{
/**
* The name of a system property that can be used to specify a comma-delimited
* list of IP addresses to use if resolution fails. This is intended
* primarily for testing purposes.
*/
@NotNull static final String PROPERTY_DEFAULT_ADDRESSES =
RoundRobinDNSServerSet.class.getName() + ".defaultAddresses";
/**
* An enum that defines the modes that may be used to select the order in
* which addresses should be used in attempts to establish connections.
*/
public enum AddressSelectionMode
{
/**
* The address selection mode that will cause addresses to be consistently
* attempted in the order they are retrieved from the name service.
*/
FAILOVER,
/**
* The address selection mode that will cause the order of addresses to be
* randomized for each attempt.
*/
RANDOM,
/**
* The address selection mode that will cause connection attempts to be made
* in a round-robin order.
*/
ROUND_ROBIN;
/**
* Retrieves the address selection mode with the specified name.
*
* @param name The name of the address selection mode to retrieve. It
* must not be {@code null}.
*
* @return The requested address selection mode, or {@code null} if no such
* change mode is defined.
*/
@Nullable()
public static AddressSelectionMode forName(@NotNull final String name)
{
switch (StaticUtils.toLowerCase(name))
{
case "failover":
return FAILOVER;
case "random":
return RANDOM;
case "roundrobin":
case "round-robin":
case "round_robin":
return ROUND_ROBIN;
default:
return null;
}
}
}
// The address selection mode that should be used if the provided hostname
// resolves to multiple addresses.
@NotNull private final AddressSelectionMode selectionMode;
// A counter that will be used to handle round-robin ordering.
@NotNull private final AtomicLong roundRobinCounter;
// A reference to an object that combines the resolved addresses with a
// timestamp indicating when the value should no longer be trusted.
@NotNull private final AtomicReference>
resolvedAddressesWithTimeout;
// The bind request to use to authenticate connections created by this
// server set.
@Nullable private final BindRequest bindRequest;
// The properties that will be used to initialize the JNDI context, if any.
@Nullable private final Hashtable jndiProperties;
// The port number for the target server.
private final int port;
// The set of connection options to use for new connections.
@NotNull private final LDAPConnectionOptions connectionOptions;
// The maximum length of time, in milliseconds, to cache resolved addresses.
private final long cacheTimeoutMillis;
// The post-connect processor to invoke against connections created by this
// server set.
@Nullable private final PostConnectProcessor postConnectProcessor;
// The socket factory to use to establish connections.
@NotNull private final SocketFactory socketFactory;
// The hostname to be resolved.
@NotNull private final String hostname;
// The provider URL to use to resolve names, if any.
@Nullable private final String providerURL;
// The DNS record types that will be used to obtain the IP addresses for the
// specified hostname.
@NotNull private final String[] dnsRecordTypes;
/**
* Creates a new round-robin DNS server set with the provided information.
*
* @param hostname The hostname to be resolved to one or more
* addresses. It must not be {@code null}.
* @param port The port to use to connect to the server. Note
* that even if the provided hostname resolves to
* multiple addresses, the same port must be used
* for all addresses.
* @param selectionMode The selection mode that should be used if the
* hostname resolves to multiple addresses. It
* must not be {@code null}.
* @param cacheTimeoutMillis The maximum length of time in milliseconds to
* cache addresses resolved from the provided
* hostname. Caching resolved addresses can
* result in better performance and can reduce the
* number of requests to the name service. A
* that is less than or equal to zero indicates
* that no caching should be used.
* @param providerURL The JNDI provider URL that should be used when
* communicating with the DNS server. If this is
* {@code null}, then the underlying system's
* name service mechanism will be used (which may
* make use of other services instead of or in
* addition to DNS). If this is non-{@code null},
* then only DNS will be used to perform the name
* resolution. A value of "dns:" indicates that
* the underlying system's DNS configuration
* should be used.
* @param socketFactory The socket factory to use to establish the
* connections. It may be {@code null} if the
* JVM-default socket factory should be used.
* @param connectionOptions The set of connection options that should be
* used for the connections. It may be
* {@code null} if a default set of connection
* options should be used.
*/
public RoundRobinDNSServerSet(@NotNull final String hostname, final int port,
@NotNull final AddressSelectionMode selectionMode,
final long cacheTimeoutMillis,
@Nullable final String providerURL,
@Nullable final SocketFactory socketFactory,
@Nullable final LDAPConnectionOptions connectionOptions)
{
this(hostname, port, selectionMode, cacheTimeoutMillis, providerURL,
null, null, socketFactory, connectionOptions);
}
/**
* Creates a new round-robin DNS server set with the provided information.
*
* @param hostname The hostname to be resolved to one or more
* addresses. It must not be {@code null}.
* @param port The port to use to connect to the server. Note
* that even if the provided hostname resolves to
* multiple addresses, the same port must be used
* for all addresses.
* @param selectionMode The selection mode that should be used if the
* hostname resolves to multiple addresses. It
* must not be {@code null}.
* @param cacheTimeoutMillis The maximum length of time in milliseconds to
* cache addresses resolved from the provided
* hostname. Caching resolved addresses can
* result in better performance and can reduce the
* number of requests to the name service. A
* that is less than or equal to zero indicates
* that no caching should be used.
* @param providerURL The JNDI provider URL that should be used when
* communicating with the DNS server.If both
* {@code providerURL} and {@code jndiProperties}
* are {@code null}, then then JNDI will not be
* used to interact with DNS and the hostname
* resolution will be performed via the underlying
* system's name service mechanism (which may make
* use of other services instead of or in addition
* to DNS). If this is non-{@code null}, then
* only DNS will be used to perform the name
* resolution. A value of "dns:" indicates that
* the underlying system's DNS configuration
* should be used.
* @param jndiProperties A set of JNDI-related properties that should be
* be used when initializing the context for
* interacting with the DNS server via JNDI. If
* both {@code providerURL} and
* {@code jndiProperties} are {@code null}, then
* then JNDI will not be used to interact with
* DNS and the hostname resolution will be
* performed via the underlying system's name
* service mechanism (which may make use of other
* services instead of or in addition to DNS). If
* {@code providerURL} is {@code null} and
* {@code jndiProperties} is non-{@code null},
* then the provided properties must specify the
* URL.
* @param dnsRecordTypes Specifies the types of DNS records that will be
* used to obtain the addresses for the specified
* hostname. This will only be used if at least
* one of {@code providerURL} and
* {@code jndiProperties} is non-{@code null}. If
* this is {@code null} or empty, then a default
* record type of "A" (indicating IPv4 addresses)
* will be used.
* @param socketFactory The socket factory to use to establish the
* connections. It may be {@code null} if the
* JVM-default socket factory should be used.
* @param connectionOptions The set of connection options that should be
* used for the connections. It may be
* {@code null} if a default set of connection
* options should be used.
*/
public RoundRobinDNSServerSet(@NotNull final String hostname, final int port,
@NotNull final AddressSelectionMode selectionMode,
final long cacheTimeoutMillis,
@Nullable final String providerURL,
@Nullable final Properties jndiProperties,
@Nullable final String[] dnsRecordTypes,
@Nullable final SocketFactory socketFactory,
@Nullable final LDAPConnectionOptions connectionOptions)
{
this(hostname, port, selectionMode, cacheTimeoutMillis, providerURL,
jndiProperties, dnsRecordTypes, socketFactory, connectionOptions, null,
null);
}
/**
* Creates a new round-robin DNS server set with the provided information.
*
* @param hostname The hostname to be resolved to one or more
* addresses. It must not be {@code null}.
* @param port The port to use to connect to the server.
* Note that even if the provided hostname
* resolves to multiple addresses, the same
* port must be used for all addresses.
* @param selectionMode The selection mode that should be used if the
* hostname resolves to multiple addresses. It
* must not be {@code null}.
* @param cacheTimeoutMillis The maximum length of time in milliseconds to
* cache addresses resolved from the provided
* hostname. Caching resolved addresses can
* result in better performance and can reduce
* the number of requests to the name service.
* A that is less than or equal to zero
* indicates that no caching should be used.
* @param providerURL The JNDI provider URL that should be used
* when communicating with the DNS server. If
* both {@code providerURL} and
* {@code jndiProperties} are {@code null},
* then then JNDI will not be used to interact
* with DNS and the hostname resolution will be
* performed via the underlying system's name
* service mechanism (which may make use of
* other services instead of or in addition to
* DNS). If this is non-{@code null}, then only
* DNS will be used to perform the name
* resolution. A value of "dns:" indicates that
* the underlying system's DNS configuration
* should be used.
* @param jndiProperties A set of JNDI-related properties that should
* be used when initializing the context for
* interacting with the DNS server via JNDI. If
* both {@code providerURL} and
* {@code jndiProperties} are {@code null}, then
* JNDI will not be used to interact with DNS
* and the hostname resolution will be
* performed via the underlying system's name
* service mechanism (which may make use of
* other services instead of or in addition to
* DNS). If {@code providerURL} is
* {@code null} and {@code jndiProperties} is
* non-{@code null}, then the provided
* properties must specify the URL.
* @param dnsRecordTypes Specifies the types of DNS records that will
* be used to obtain the addresses for the
* specified hostname. This will only be used
* if at least one of {@code providerURL} and
* {@code jndiProperties} is non-{@code null}.
* If this is {@code null} or empty, then a
* default record type of "A" (indicating IPv4
* addresses) will be used.
* @param socketFactory The socket factory to use to establish the
* connections. It may be {@code null} if the
* JVM-default socket factory should be used.
* @param connectionOptions The set of connection options that should be
* used for the connections. It may be
* {@code null} if a default set of connection
* options should be used.
* @param bindRequest The bind request that should be used to
* authenticate newly-established connections.
* It may be {@code null} if this server set
* should not perform any authentication.
* @param postConnectProcessor The post-connect processor that should be
* invoked on newly-established connections. It
* may be {@code null} if this server set should
* not perform any post-connect processing.
*/
public RoundRobinDNSServerSet(@NotNull final String hostname, final int port,
@NotNull final AddressSelectionMode selectionMode,
final long cacheTimeoutMillis,
@Nullable final String providerURL,
@Nullable final Properties jndiProperties,
@Nullable final String[] dnsRecordTypes,
@Nullable final SocketFactory socketFactory,
@Nullable final LDAPConnectionOptions connectionOptions,
@Nullable final BindRequest bindRequest,
@Nullable final PostConnectProcessor postConnectProcessor)
{
Validator.ensureNotNull(hostname);
Validator.ensureTrue((port >= 1) && (port <= 65_535));
Validator.ensureNotNull(selectionMode);
this.hostname = hostname;
this.port = port;
this.selectionMode = selectionMode;
this.providerURL = providerURL;
this.bindRequest = bindRequest;
this.postConnectProcessor = postConnectProcessor;
if (jndiProperties == null)
{
if (providerURL == null)
{
this.jndiProperties = null;
}
else
{
this.jndiProperties = new Hashtable<>(2);
this.jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.dns.DnsContextFactory");
this.jndiProperties.put(Context.PROVIDER_URL, providerURL);
}
}
else
{
this.jndiProperties = new Hashtable<>(jndiProperties.size()+2);
for (final Map.Entry