/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2009-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2016 ForgeRock AS.
*/
package org.forgerock.opendj.examples;
import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
import static org.forgerock.opendj.ldap.LDAPListener.*;
import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.Connections;
import org.forgerock.opendj.ldap.LDAPClientContext;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LDAPListener;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.RequestContext;
import org.forgerock.opendj.ldap.RequestHandlerFactory;
import org.forgerock.opendj.ldap.ServerConnectionFactory;
import org.forgerock.opendj.ldap.requests.BindRequest;
import org.forgerock.util.Options;
import com.forgerock.reactive.ServerConnectionFactoryAdapter;
/**
* An LDAP load balancing proxy which forwards requests to one or more remote
* Directory Servers. This is implementation is very simple and is only intended
* as an example:
*
* - It does not support SSL connections
*
- It does not support StartTLS
*
- It does not support Abandon or Cancel requests
*
- Very basic authentication and authorization support.
*
* This example takes the following command line parameters:
*
*
* {@code [--load-balancer ]
* [ ...]}
*
*
* Where {@code } is one of "round-robin", "fail-over", or "affinity". The default is round-robin.
*/
public final class Proxy {
/**
* Main method.
*
* @param args
* The command line arguments: [--load-balancer ] listen address, listen port,
* remote address1, remote port1, remote address2, remote port2,
* ...
*/
public static void main(final String[] args) {
if (args.length < 6 || args.length % 2 != 0) {
System.err.println("Usage: [--load-balancer ] listenAddress listenPort "
+ "proxyDN proxyPassword remoteAddress1 remotePort1 remoteAddress2 remotePort2 ...");
System.exit(1);
}
// Parse command line arguments.
int i = 0;
final LoadBalancingAlgorithm algorithm;
if ("--load-balancer".equals(args[i])) {
algorithm = getLoadBalancingAlgorithm(args[i + 1]);
i += 2;
} else {
algorithm = LoadBalancingAlgorithm.ROUND_ROBIN;
}
final String localAddress = args[i++];
final int localPort = Integer.parseInt(args[i++]);
final String proxyDN = args[i++];
final String proxyPassword = args[i++];
// Create load balancer.
// --- JCite pools ---
final List factories = new LinkedList<>();
final BindRequest bindRequest = newSimpleBindRequest(proxyDN, proxyPassword.toCharArray());
final Options factoryOptions = Options.defaultOptions()
.set(HEARTBEAT_ENABLED, true)
.set(AUTHN_BIND_REQUEST, bindRequest);
final List bindFactories = new LinkedList<>();
final Options bindFactoryOptions = Options.defaultOptions().set(HEARTBEAT_ENABLED, true);
for (; i < args.length; i += 2) {
final String remoteAddress = args[i];
final int remotePort = Integer.parseInt(args[i + 1]);
factories.add(Connections.newCachedConnectionPool(new LDAPConnectionFactory(remoteAddress,
remotePort,
factoryOptions)));
bindFactories.add(Connections.newCachedConnectionPool(new LDAPConnectionFactory(remoteAddress,
remotePort,
bindFactoryOptions)));
}
// --- JCite pools ---
final ConnectionFactory factory = algorithm.newLoadBalancer(factories, factoryOptions);
final ConnectionFactory bindFactory = algorithm.newLoadBalancer(bindFactories, bindFactoryOptions);
// --- JCite backend ---
/*
* Create a server connection adapter which will create a new proxy
* backend for each inbound client connection. This is required because
* we need to maintain authorization state between client requests.
*/
final RequestHandlerFactory proxyFactory =
new RequestHandlerFactory() {
@Override
public ProxyBackend handleAccept(LDAPClientContext clientContext)
throws LdapException {
return new ProxyBackend(factory, bindFactory);
}
};
final ServerConnectionFactory connectionHandler =
Connections.newServerConnectionFactory(proxyFactory);
// --- JCite backend ---
// --- JCite listener ---
// Create listener.
final Options options = Options.defaultOptions().set(CONNECT_MAX_BACKLOG, 4096);
LDAPListener listener = null;
try {
listener = new LDAPListener(localAddress, localPort, new ServerConnectionFactoryAdapter(
options.get(LDAP_DECODE_OPTIONS), 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();
}
}
// --- JCite listener ---
}
private static LoadBalancingAlgorithm getLoadBalancingAlgorithm(final String algorithmName) {
switch (algorithmName) {
case "round-robin":
return LoadBalancingAlgorithm.ROUND_ROBIN;
case "fail-over":
return LoadBalancingAlgorithm.FAIL_OVER;
case "affinity":
return LoadBalancingAlgorithm.AFFINITY;
default:
System.err.println("Unrecognized load-balancing algorithm '" + algorithmName + "'. Should be one of "
+ "'round-robin', 'fail-over', or 'affinity'.");
System.exit(1);
}
return LoadBalancingAlgorithm.ROUND_ROBIN; // keep compiler happy.
}
private enum LoadBalancingAlgorithm {
ROUND_ROBIN {
@Override
ConnectionFactory newLoadBalancer(final Collection factories, final Options options) {
// --- JCite load balancer ---
return Connections.newRoundRobinLoadBalancer(factories, options);
// --- JCite load balancer ---
}
},
FAIL_OVER {
@Override
ConnectionFactory newLoadBalancer(final Collection factories, final Options options) {
return Connections.newFailoverLoadBalancer(factories, options);
}
},
AFFINITY {
@Override
ConnectionFactory newLoadBalancer(final Collection factories, final Options options) {
return Connections.newAffinityRequestLoadBalancer(factories, options);
}
};
abstract ConnectionFactory newLoadBalancer(Collection factories, Options options);
}
private Proxy() {
// Not used.
}
}