001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.examples;
029
030import java.io.IOException;
031import java.util.LinkedList;
032import java.util.List;
033
034import org.forgerock.opendj.ldap.ConnectionFactory;
035import org.forgerock.opendj.ldap.Connections;
036import org.forgerock.opendj.ldap.LdapException;
037import org.forgerock.opendj.ldap.LDAPClientContext;
038import org.forgerock.opendj.ldap.LDAPConnectionFactory;
039import org.forgerock.opendj.ldap.LDAPListener;
040import org.forgerock.opendj.ldap.LDAPListenerOptions;
041import org.forgerock.opendj.ldap.RequestContext;
042import org.forgerock.opendj.ldap.RequestHandlerFactory;
043import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
044import org.forgerock.opendj.ldap.ServerConnectionFactory;
045import org.forgerock.opendj.ldap.requests.Requests;
046
047/**
048 * An LDAP load balancing proxy which forwards requests to one or more remote
049 * Directory Servers. This is implementation is very simple and is only intended
050 * as an example:
051 * <ul>
052 * <li>It does not support SSL connections
053 * <li>It does not support StartTLS
054 * <li>It does not support Abandon or Cancel requests
055 * <li>Very basic authentication and authorization support.
056 * </ul>
057 * This example takes the following command line parameters:
058 *
059 * <pre>
060 *     {@code <listenAddress> <listenPort> <proxyDN> <proxyPassword> <remoteAddress1> <remotePort1>
061 *      [<remoteAddress2> <remotePort2> ...]}
062 * </pre>
063 */
064public final class Proxy {
065    /**
066     * Main method.
067     *
068     * @param args
069     *            The command line arguments: listen address, listen port,
070     *            remote address1, remote port1, remote address2, remote port2,
071     *            ...
072     */
073    public static void main(final String[] args) {
074        if (args.length < 6 || args.length % 2 != 0) {
075            System.err.println("Usage: listenAddress listenPort "
076                    + "proxyDN proxyPassword remoteAddress1 remotePort1 "
077                    + "remoteAddress2 remotePort2 ...");
078            System.exit(1);
079        }
080
081        // Parse command line arguments.
082        final String localAddress = args[0];
083        final int localPort = Integer.parseInt(args[1]);
084
085        final String proxyDN = args[2];
086        final String proxyPassword = args[3];
087
088        // Create load balancer.
089        // --- JCite pools ---
090        final List<ConnectionFactory> factories = new LinkedList<>();
091        final List<ConnectionFactory> bindFactories = new LinkedList<>();
092        for (int i = 4; i < args.length; i += 2) {
093            final String remoteAddress = args[i];
094            final int remotePort = Integer.parseInt(args[i + 1]);
095
096            factories.add(Connections.newCachedConnectionPool(Connections
097                    .newAuthenticatedConnectionFactory(Connections
098                            .newHeartBeatConnectionFactory(new LDAPConnectionFactory(remoteAddress,
099                                    remotePort)), Requests.newSimpleBindRequest(proxyDN,
100                            proxyPassword.toCharArray()))));
101            bindFactories.add(Connections.newCachedConnectionPool(Connections
102                    .newHeartBeatConnectionFactory(new LDAPConnectionFactory(remoteAddress,
103                            remotePort))));
104        }
105        // --- JCite pools ---
106
107        // --- JCite load balancer ---
108        final RoundRobinLoadBalancingAlgorithm algorithm =
109                new RoundRobinLoadBalancingAlgorithm(factories);
110        final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
111                new RoundRobinLoadBalancingAlgorithm(bindFactories);
112        final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
113        final ConnectionFactory bindFactory = Connections.newLoadBalancer(bindAlgorithm);
114        // --- JCite load balancer ---
115
116        // --- JCite backend ---
117        /*
118         * Create a server connection adapter which will create a new proxy
119         * backend for each inbound client connection. This is required because
120         * we need to maintain authorization state between client requests.
121         */
122        final RequestHandlerFactory<LDAPClientContext, RequestContext> proxyFactory =
123                new RequestHandlerFactory<LDAPClientContext, RequestContext>() {
124                    @Override
125                    public ProxyBackend handleAccept(LDAPClientContext clientContext)
126                            throws LdapException {
127                        return new ProxyBackend(factory, bindFactory);
128                    }
129                };
130        final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
131                Connections.newServerConnectionFactory(proxyFactory);
132        // --- JCite backend ---
133
134        // --- JCite listener ---
135        // Create listener.
136        final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
137        LDAPListener listener = null;
138        try {
139            listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
140            System.out.println("Press any key to stop the server...");
141            System.in.read();
142        } catch (final IOException e) {
143            System.out.println("Error listening on " + localAddress + ":" + localPort);
144            e.printStackTrace();
145        } finally {
146            if (listener != null) {
147                listener.close();
148            }
149        }
150        // --- JCite listener ---
151    }
152
153    private Proxy() {
154        // Not used.
155    }
156}