001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009-2010 Sun Microsystems, Inc. 015 * Portions copyright 2012-2015 ForgeRock AS. 016 */ 017package org.forgerock.opendj.ldap; 018 019import java.io.Closeable; 020import java.io.IOException; 021import java.net.InetAddress; 022import java.net.InetSocketAddress; 023 024import org.forgerock.opendj.ldap.spi.LDAPListenerImpl; 025import org.forgerock.opendj.ldap.spi.TransportProvider; 026import org.forgerock.util.Option; 027import org.forgerock.util.Options; 028import org.forgerock.util.Reject; 029 030/** 031 * An LDAP server connection listener which waits for LDAP connection requests 032 * to come in over the network and binds them to a {@link ServerConnection} 033 * created using the provided {@link ServerConnectionFactory}. 034 * <p> 035 * When processing requests, {@code ServerConnection} implementations are passed 036 * an integer as the first parameter. This integer represents the 037 * {@code requestID} associated with the client request and corresponds to the 038 * {@code requestID} passed as a parameter to abandon and cancel extended 039 * requests. The request ID may also be useful for logging purposes. 040 * <p> 041 * An {@code LDAPListener} does not require {@code ServerConnection} 042 * implementations to return a result when processing requests. More 043 * specifically, an {@code LDAPListener} does not maintain any internal state 044 * information associated with each request which must be released. This is 045 * useful when implementing LDAP abandon operations which may prevent results 046 * being sent for abandoned operations. 047 * <p> 048 * The following code illustrates how to create a simple LDAP server: 049 * 050 * <pre> 051 * class MyClientConnection implements ServerConnection<Integer> { 052 * private final LDAPClientContext clientContext; 053 * 054 * private MyClientConnection(LDAPClientContext clientContext) { 055 * this.clientContext = clientContext; 056 * } 057 * 058 * public void add(Integer requestID, AddRequest request, ResultHandler<Result> handler, 059 * IntermediateResponseHandler intermediateResponseHandler) 060 * throws UnsupportedOperationException { 061 * // ... 062 * } 063 * 064 * // ... 065 * 066 * } 067 * 068 * class MyServer implements ServerConnectionFactory<LDAPClientContext, RequestContext> { 069 * public ServerConnection<RequestContext> accept(LDAPClientContext context) { 070 * System.out.println("Connection from: " + context.getPeerAddress()); 071 * return new MyClientConnection(context); 072 * } 073 * } 074 * 075 * public static void main(String[] args) throws Exception { 076 * LDAPListener listener = new LDAPListener(1389, new MyServer()); 077 * 078 * // ... 079 * 080 * listener.close(); 081 * } 082 * </pre> 083 */ 084public final class LDAPListener extends CommonLDAPOptions implements Closeable { 085 086 /** 087 * Specifies the maximum queue length for incoming connections requests. If a 088 * connection request arrives when the queue is full, the connection is refused. 089 */ 090 public static final Option<Integer> CONNECT_MAX_BACKLOG = Option.withDefault(50); 091 092 /** 093 * Specifies the maximum request size in bytes for incoming LDAP requests. 094 * If an incoming request exceeds the limit then the connection will be aborted by the listener. 095 * Default value is 5MiB. 096 */ 097 public static final Option<Integer> REQUEST_MAX_SIZE_IN_BYTES = Option.withDefault(5 * 1024 * 1024); 098 099 /** 100 * We implement the factory using the pimpl idiom in order have 101 * cleaner Javadoc which does not expose implementation methods. 102 */ 103 private final LDAPListenerImpl impl; 104 105 /** Transport provider that provides the implementation of this listener. */ 106 private TransportProvider provider; 107 108 /** 109 * Creates a new LDAP listener implementation which will listen for LDAP 110 * client connections at the provided address. 111 * 112 * @param port 113 * The port to listen on. 114 * @param factory 115 * The server connection factory which will be used to create 116 * server connections. 117 * @throws IOException 118 * If an error occurred while trying to listen on the provided 119 * address. 120 * @throws NullPointerException 121 * If {code factory} was {@code null}. 122 */ 123 public LDAPListener(final int port, 124 final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { 125 this(port, factory, Options.defaultOptions()); 126 } 127 128 /** 129 * Creates a new LDAP listener implementation which will listen for LDAP 130 * client connections at the provided address. 131 * 132 * @param port 133 * The port to listen on. 134 * @param factory 135 * The server connection factory which will be used to create 136 * server connections. 137 * @param options 138 * The LDAP listener options. 139 * @throws IOException 140 * If an error occurred while trying to listen on the provided 141 * address. 142 * @throws NullPointerException 143 * If {code factory} or {@code options} was {@code null}. 144 */ 145 public LDAPListener(final int port, 146 final ServerConnectionFactory<LDAPClientContext, Integer> factory, 147 final Options options) throws IOException { 148 Reject.ifNull(factory, options); 149 this.provider = getTransportProvider(options); 150 this.impl = provider.getLDAPListener(new InetSocketAddress(port), factory, options); 151 } 152 153 /** 154 * Creates a new LDAP listener implementation which will listen for LDAP 155 * client connections at the provided address. 156 * 157 * @param address 158 * The address to listen on. 159 * @param factory 160 * The server connection factory which will be used to create 161 * server connections. 162 * @throws IOException 163 * If an error occurred while trying to listen on the provided 164 * address. 165 * @throws NullPointerException 166 * If {@code address} or {code factory} was {@code null}. 167 */ 168 public LDAPListener(final InetSocketAddress address, 169 final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { 170 this(address, factory, Options.defaultOptions()); 171 } 172 173 /** 174 * Creates a new LDAP listener implementation which will listen for LDAP 175 * client connections at the provided address. 176 * 177 * @param address 178 * The address to listen on. 179 * @param factory 180 * The server connection factory which will be used to create 181 * server connections. 182 * @param options 183 * The LDAP listener options. 184 * @throws IOException 185 * If an error occurred while trying to listen on the provided 186 * address. 187 * @throws NullPointerException 188 * If {@code address}, {code factory}, or {@code options} was 189 * {@code null}. 190 */ 191 public LDAPListener(final InetSocketAddress address, 192 final ServerConnectionFactory<LDAPClientContext, Integer> factory, 193 final Options options) throws IOException { 194 Reject.ifNull(address, factory, options); 195 this.provider = getTransportProvider(options); 196 this.impl = provider.getLDAPListener(address, factory, options); 197 } 198 199 /** 200 * Creates a new LDAP listener implementation which will listen for LDAP 201 * client connections at the provided address. 202 * 203 * @param host 204 * The address to listen on. 205 * @param port 206 * The port to listen on. 207 * @param factory 208 * The server connection factory which will be used to create 209 * server connections. 210 * @throws IOException 211 * If an error occurred while trying to listen on the provided 212 * address. 213 * @throws NullPointerException 214 * If {@code host} or {code factory} was {@code null}. 215 */ 216 public LDAPListener(final String host, final int port, 217 final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException { 218 this(host, port, factory, Options.defaultOptions()); 219 } 220 221 /** 222 * Creates a new LDAP listener implementation which will listen for LDAP 223 * client connections at the provided address. 224 * 225 * @param host 226 * The address to listen on. 227 * @param port 228 * The port to listen on. 229 * @param factory 230 * The server connection factory which will be used to create 231 * server connections. 232 * @param options 233 * The LDAP listener options. 234 * @throws IOException 235 * If an error occurred while trying to listen on the provided 236 * address. 237 * @throws NullPointerException 238 * If {@code host}, {code factory}, or {@code options} was 239 * {@code null}. 240 */ 241 public LDAPListener(final String host, final int port, 242 final ServerConnectionFactory<LDAPClientContext, Integer> factory, 243 final Options options) throws IOException { 244 Reject.ifNull(host, factory, options); 245 final InetSocketAddress address = new InetSocketAddress(host, port); 246 this.provider = getTransportProvider(options); 247 this.impl = provider.getLDAPListener(address, factory, options); 248 } 249 250 /** Closes this LDAP connection listener. */ 251 @Override 252 public void close() { 253 impl.close(); 254 } 255 256 /** 257 * Returns the {@code InetAddress} that this LDAP listener is listening on. 258 * 259 * @return The {@code InetAddress} that this LDAP listener is listening on. 260 */ 261 public InetAddress getAddress() { 262 return getSocketAddress().getAddress(); 263 } 264 265 /** 266 * Returns the host name that this LDAP listener is listening on. The 267 * returned host name is the same host name that was provided during 268 * construction and may be an IP address. More specifically, this method 269 * will not perform a reverse DNS lookup. 270 * 271 * @return The host name that this LDAP listener is listening on. 272 */ 273 public String getHostName() { 274 return Connections.getHostString(getSocketAddress()); 275 } 276 277 /** 278 * Returns the port that this LDAP listener is listening on. 279 * 280 * @return The port that this LDAP listener is listening on. 281 */ 282 public int getPort() { 283 return getSocketAddress().getPort(); 284 } 285 286 /** 287 * Returns the address that this LDAP listener is listening on. 288 * 289 * @return The address that this LDAP listener is listening on. 290 */ 291 public InetSocketAddress getSocketAddress() { 292 return impl.getSocketAddress(); 293 } 294 295 /** 296 * Returns the name of the transport provider, which provides the implementation 297 * of this factory. 298 * 299 * @return The name of actual transport provider. 300 */ 301 public String getProviderName() { 302 return provider.getName(); 303 } 304 305 @Override 306 public String toString() { 307 return impl.toString(); 308 } 309}