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