/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2010 Sun Microsystems, Inc. */ package org.opends.sdk.examples.server.proxy; import static org.opends.sdk.ErrorResultException.newErrorResult; import java.io.IOException; import java.util.LinkedList; import java.util.List; import org.opends.sdk.*; import org.opends.sdk.requests.*; import org.opends.sdk.responses.BindResult; import org.opends.sdk.responses.CompareResult; import org.opends.sdk.responses.ExtendedResult; import org.opends.sdk.responses.Result; /** * 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: * * This example takes the following command line parameters: * *
 *  <listenAddress> <listenPort> <remoteAddress1> <remotePort1>
 *      [<remoteAddress2> <remotePort2> ...]
 * 
*/ public final class Main { /** * Proxy server connection factory implementation. */ private static final class Proxy implements ServerConnectionFactory { private final class ServerConnectionImpl implements ServerConnection { private volatile AsynchronousConnection connection = null; private volatile boolean isUnbindRequired = false; private AsynchronousConnection getConnection() throws ErrorResultException { if (connection == null) { synchronized (this) { if (connection == null) { try { connection = factory.getAsynchronousConnection(null).get(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } return connection; } private ServerConnectionImpl(final LDAPClientContext clientContext) { // Nothing to do. } /** * {@inheritDoc} */ @Override public void handleAbandon(final Integer requestContext, final AbandonRequest request) throws UnsupportedOperationException { // Not implemented. } /** * {@inheritDoc} */ @Override public void handleAdd(final Integer requestContext, final AddRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().add(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } /** * {@inheritDoc} */ @Override public void handleBind(final Integer requestContext, final int version, final BindRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { if (request.getAuthenticationType() != ((byte) 0x80)) { // TODO: SASL authentication not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "non-SIMPLE authentication not supported: " + request.getAuthenticationType())); } else { // Note that this connection has received a bind request: the // connection should be reverted back to anonymous when the client // unbinds. isUnbindRequired = true; try { getConnection().bind(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } } /** * {@inheritDoc} */ @Override public void handleCompare(final Integer requestContext, final CompareRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().compare(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } /** * {@inheritDoc} */ @Override public void handleConnectionClosed(final Integer requestContext, final UnbindRequest request) { // Client connection closed: release the proxy connection. close(); } /** * {@inheritDoc} */ @Override public void handleConnectionDisconnected(final ResultCode resultCode, final String message) { // Client disconnected by server: release the proxy connection. close(); } /** * {@inheritDoc} */ @Override public void handleConnectionError(final Throwable error) { // Client connection failed: release the proxy connection. close(); } /** * {@inheritDoc} */ @Override public void handleDelete(final Integer requestContext, final DeleteRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().delete(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } /** * {@inheritDoc} */ @Override public void handleExtendedRequest( final Integer requestContext, final ExtendedRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { if (request.getOID().equals(CancelExtendedRequest.OID)) { // TODO: not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "Cancel extended request operation not supported")); } else if (request.getOID().equals(StartTLSExtendedRequest.OID)) { // TODO: not implemented. resultHandler.handleErrorResult(newErrorResult( ResultCode.PROTOCOL_ERROR, "StartTLS extended request operation not supported")); } else { // Forward all other extended operations. try { getConnection().extendedRequest(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } } /** * {@inheritDoc} */ @Override public void handleModify(final Integer requestContext, final ModifyRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().modify(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } /** * {@inheritDoc} */ @Override public void handleModifyDN(final Integer requestContext, final ModifyDNRequest request, final ResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().modifyDN(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } /** * {@inheritDoc} */ @Override public void handleSearch(final Integer requestContext, final SearchRequest request, final SearchResultHandler resultHandler, final IntermediateResponseHandler intermediateResponseHandler) throws UnsupportedOperationException { try { getConnection().search(request, resultHandler, intermediateResponseHandler); } catch (ErrorResultException e) { resultHandler.handleErrorResult(e); } } private void close() { if (isUnbindRequired) { synchronized (this) { if (connection != null) { connection.bind(Requests.newSimpleBindRequest(), new ResultHandler() { public void handleErrorResult(ErrorResultException error) { // The rebind failed - this is bad because if the // connection is pooled it will remain authenticated as // the wrong user. handleResult(error.getResult()); } public void handleResult(Result result) { synchronized (ServerConnectionImpl.this) { if (connection != null) { connection.close(); } } } }); } } } } } private final ConnectionFactory factory; private Proxy(final ConnectionFactory factory) { this.factory = factory; } /** * {@inheritDoc} */ @Override public ServerConnection handleAccept( final LDAPClientContext clientContext) throws ErrorResultException { return new ServerConnectionImpl(clientContext); } } /** * Main method. * * @param args * The command line arguments: listen address, listen port, remote * address1, remote port1, remote address2, remote port2, ... */ public static void main(final String[] args) { if (args.length < 4 || args.length % 2 != 0) { System.err.println("Usage: listenAddress listenPort " + "remoteAddress1 remotePort1 remoteAddress2 remotePort2"); System.exit(1); } // Parse command line arguments. final String localAddress = args[0]; final int localPort = Integer.parseInt(args[1]); // Create load balancer. final List factories = new LinkedList(); for (int i = 2; i < args.length; i += 2) { final String remoteAddress = args[i]; final int remotePort = Integer.parseInt(args[i + 1]); // factories.add(Connections.newConnectionPool(new LDAPConnectionFactory( // remoteAddress, remotePort), Integer.MAX_VALUE)); factories.add(new LDAPConnectionFactory(remoteAddress, remotePort)); } final RoundRobinLoadBalancingAlgorithm algorithm = new RoundRobinLoadBalancingAlgorithm( factories); final ConnectionFactory factory = Connections.newLoadBalancer(algorithm); // Create listener. final LDAPListenerOptions options = new LDAPListenerOptions() .setBacklog(4096); LDAPListener listener = null; try { listener = new LDAPListener(localAddress, localPort, new Proxy(factory), 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(); } } } private Main() { // Not used. } }