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 static org.forgerock.opendj.ldap.LDAPConnectionFactory.AUTHN_BIND_REQUEST;
031import static org.forgerock.opendj.ldap.LDAPConnectionFactory.HEARTBEAT_ENABLED;
032import static org.forgerock.opendj.ldap.LDAPListener.*;
033import static org.forgerock.opendj.ldap.requests.Requests.newSimpleBindRequest;
034
035import java.io.IOException;
036import java.util.LinkedList;
037import java.util.List;
038
039import org.forgerock.opendj.ldap.ConnectionFactory;
040import org.forgerock.opendj.ldap.Connections;
041import org.forgerock.opendj.ldap.LdapException;
042import org.forgerock.opendj.ldap.LDAPClientContext;
043import org.forgerock.opendj.ldap.LDAPConnectionFactory;
044import org.forgerock.opendj.ldap.LDAPListener;
045import org.forgerock.opendj.ldap.RequestContext;
046import org.forgerock.opendj.ldap.RequestHandlerFactory;
047import org.forgerock.opendj.ldap.ServerConnectionFactory;
048import org.forgerock.opendj.ldap.requests.BindRequest;
049import org.forgerock.util.Options;
050
051/**
052 * An LDAP load balancing proxy which forwards requests to one or more remote
053 * Directory Servers. This is implementation is very simple and is only intended
054 * as an example:
055 * <ul>
056 * <li>It does not support SSL connections
057 * <li>It does not support StartTLS
058 * <li>It does not support Abandon or Cancel requests
059 * <li>Very basic authentication and authorization support.
060 * </ul>
061 * This example takes the following command line parameters:
062 *
063 * <pre>
064 *     {@code <listenAddress> <listenPort> <proxyDN> <proxyPassword> <remoteAddress1> <remotePort1>
065 *      [<remoteAddress2> <remotePort2> ...]}
066 * </pre>
067 */
068public final class Proxy {
069    /**
070     * Main method.
071     *
072     * @param args
073     *            The command line arguments: listen address, listen port,
074     *            remote address1, remote port1, remote address2, remote port2,
075     *            ...
076     */
077    public static void main(final String[] args) {
078        if (args.length < 6 || args.length % 2 != 0) {
079            System.err.println("Usage: listenAddress listenPort "
080                    + "proxyDN proxyPassword remoteAddress1 remotePort1 "
081                    + "remoteAddress2 remotePort2 ...");
082            System.exit(1);
083        }
084
085        // Parse command line arguments.
086        final String localAddress = args[0];
087        final int localPort = Integer.parseInt(args[1]);
088
089        final String proxyDN = args[2];
090        final String proxyPassword = args[3];
091
092        // Create load balancer.
093        // --- JCite pools ---
094        final List<ConnectionFactory> factories = new LinkedList<>();
095        final BindRequest bindRequest = newSimpleBindRequest(proxyDN, proxyPassword.toCharArray());
096        final Options factoryOptions = Options.defaultOptions()
097                                              .set(HEARTBEAT_ENABLED, true)
098                                              .set(AUTHN_BIND_REQUEST, bindRequest);
099
100        final List<ConnectionFactory> bindFactories = new LinkedList<>();
101        final Options bindFactoryOptions = Options.defaultOptions().set(HEARTBEAT_ENABLED, true);
102
103        for (int i = 4; i < args.length; i += 2) {
104            final String remoteAddress = args[i];
105            final int remotePort = Integer.parseInt(args[i + 1]);
106
107            factories.add(Connections.newCachedConnectionPool(new LDAPConnectionFactory(remoteAddress,
108                                                                                        remotePort,
109                                                                                        factoryOptions)));
110
111            bindFactories.add(Connections.newCachedConnectionPool(new LDAPConnectionFactory(remoteAddress,
112                                                                                            remotePort,
113                                                                                            bindFactoryOptions)));
114        }
115        // --- JCite pools ---
116
117        // --- JCite load balancer ---
118        final ConnectionFactory factory = Connections.newRoundRobinLoadBalancer(factories, factoryOptions);
119        final ConnectionFactory bindFactory = Connections.newRoundRobinLoadBalancer(bindFactories, bindFactoryOptions);
120        // --- JCite load balancer ---
121
122        // --- JCite backend ---
123        /*
124         * Create a server connection adapter which will create a new proxy
125         * backend for each inbound client connection. This is required because
126         * we need to maintain authorization state between client requests.
127         */
128        final RequestHandlerFactory<LDAPClientContext, RequestContext> proxyFactory =
129                new RequestHandlerFactory<LDAPClientContext, RequestContext>() {
130                    @Override
131                    public ProxyBackend handleAccept(LDAPClientContext clientContext)
132                            throws LdapException {
133                        return new ProxyBackend(factory, bindFactory);
134                    }
135                };
136        final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
137                Connections.newServerConnectionFactory(proxyFactory);
138        // --- JCite backend ---
139
140        // --- JCite listener ---
141        // Create listener.
142        final Options options = Options.defaultOptions().set(CONNECT_MAX_BACKLOG, 4096);
143        LDAPListener listener = null;
144        try {
145            listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
146            System.out.println("Press any key to stop the server...");
147            System.in.read();
148        } catch (final IOException e) {
149            System.out.println("Error listening on " + localAddress + ":" + localPort);
150            e.printStackTrace();
151        } finally {
152            if (listener != null) {
153                listener.close();
154            }
155        }
156        // --- JCite listener ---
157    }
158
159    private Proxy() {
160        // Not used.
161    }
162}