The OpenDJ LDAP SDK example Proxy demonstrates a simple LDAP proxy that forwards requests to one or more remote directory servers. Although the implementation is intended as an example, it does demonstrate use of the asynchronous API, load balancing, and connection pooling.
The Proxy example sets up connections pools with load balancing to the
directory servers. It passes the connection factories to a
ProxyBackend
that handles the requests passed back
to the directory servers. It also sets up an LDAP listener to receive incoming
connections from clients of the Proxy.
The ProxyBackend
uses separate connection factories,
one for bind operations, the other for other operations. It uses the proxied
authorization control to ensure operations are performed using the bind
identity for the operation.
The ProxyBackend
's function is to handle each client
request, encapsulating the result handlers that allow it to deal with each
basic operation. It authenticates to the directory server to check incoming
credentials, and adds the proxied authorization control to requests other than
binds. The ProxyBackend
handles all operations using
asynchronous connections and methods.
As shown in the Proxy example, the
Connections.newFixedConnectionPool()
returns a connection
pool of the maximum size you specify.
final List<ConnectionFactory> factories = new LinkedList<~>(); factories.add(Connections.newFixedConnectionPool(Connections .newAuthenticatedConnectionFactory(Connections .newHeartBeatConnectionFactory(new LDAPConnectionFactory( remoteAddress, remotePort)), Requests.newSimpleBindRequest(proxyDN, proxyPassword.toCharArray())), Integer.MAX_VALUE));
Connections are returned to the pool when you close()
them. Notice that Connections
also provides methods to
return ConnectionFactory
s with a heart beat check on
connections provided by the factory, and connection factories that
authenticate connections before returning them.
Connections in the pool are intended for reuse. The Proxy gets an authenticated connection, which is a connection where the OpenDJ LDAP SDK passes a bind request immediately when getting the connection. The Proxy then uses proxied authorization to handle the identity from the client requesting the operation. As a rule, either handle binds separately and use proxied authorization as in the Proxy example, or else make sure that the first operation on a connection retrieved from the pool is a bind that correctly authenticates the user currently served by the connection.
When you close()
a connection from the pool, the
OpenDJ LDAP SDK does not perform an unbind()
. This is why
you must be careful about how you manage authentication on connections from a
pool.
The Connections.newLoadBalancer()
method returns a
load balancer based on the algorithm you choose. Algorithms include both
round robin for equitably sharing load across local directory servers, and
also failover usually used for switching automatically from an unresponsive
server group to an alternative server group. The algorithms take collections
of connection factories, such as those that you set up for connection
pooling.
The following excerpt shows how to set up round robin load balancing across directory servers.
final List<ConnectionFactory> factories = new LinkedList<ConnectionFactory>(); // Set up a ConnectionFactory for each directory server in the pool as shown in // the previous example, and then set up a load balancer. final RoundRobinLoadBalancingAlgorithm algorithm = new RoundRobinLoadBalancingAlgorithm(factories); final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
With multiple pools of directory servers, for example in a deployment across multiple data centers, also use fail over load balancing. Fail over load balancing directs all requests to the first (preferred) pool of servers until problems are encountered with the connections to that pool. Then it fails over to the next pool in the list. Therefore in each data center you can set up round robin load balancing, and then set up fail over load balancing across data centers.
// localFactory: ConnectionFactory to servers in the local data center // remoteFactory: ConnectionFactory for servers in a remote data center // localFactory and remoteFactory use round robin load balancing "internally". final List<ConnectionFactory> factories = Arrays.asList(localFactory, remoteFactory); final FailoverLoadBalancingAlgorithm algorithm = new FailoverLoadBalancingAlgorithm(factories); final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
The algorithms also include constructors that let you adjust timeouts and so forth.
You create an LDAPListener
to handle incoming client
connections. The LDAPListener
takes a connection handler
that deals with the connections, in this case connections back to the
directory servers handling client requests.
final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096); LDAPListener listener = null; try { listener = new LDAPListener(localAddress, localPort, connectionHandler, options); System.out.println("Press any key to stop the server..."); System.in.read(); } catch (final IOException e) { System.out.println("Error listening on " + localAddress + ":" + localPort); e.printStackTrace(); } finally { if (listener != null) { listener.close(); } }
You get a ServerConnectionFactory
to handle requests
coming from clients. The ServerConnectionFactory
takes a
request handler that deals with the incoming client requests. The request
handler implements handlers for all supported operations. The Proxy example
implements a ProxyBackend
to handle requests. The
ProxyBackend
sends the requests on to the backend
directory servers and routes the results returned back to client
applications.
final ProxyBackend backend = new ProxyBackend(factory, bindFactory); final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler = Connections.newServerConnectionFactory(backend);
See the Proxy example code for details about the
ProxyBackend
implementation.
Suppose you have a client application that expects a different
attribute name, such as fullname
for a standard attribute
like cn
(common name), and that expects a distinguished
name (DN) suffix different from what is stored in the directory. If you
cannot change the application, one possible alternative is a proxy layer
that does DN and attribute rewriting.[9]
# A search accessing the directory server $ ldapsearch -b dc=example,dc=com -p 1389 "(cn=Babs Jensen)" cn dn: uid=bjensen,ou=People,dc=example,dc=com cn: Barbara Jensen cn: Babs Jensen # The same search search accessing a proxy that rewrites requests and responses $ ldapsearch -b o=example -p 8389 "(fullname=Babs Jensen)" fullname dn: uid=bjensen,ou=People,o=example fullname: Barbara Jensen fullname: Babs Jensen
The OpenDJ LDAP SDK RewriterProxy example builds on the Proxy example, rewriting requests
and search result entries. When you read the example, look for the
rewrite()
methods.
In the above output, the rewriter proxy listens on port 8389,
connecting to a directory server listening on 1389. The directory server
contains data from Example.ldif
.
[9] Some servers, such as OpenDJ directory server, can do attribute rewriting without a proxy layer. See your directory server's documentation for details.