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&lt;Integer&gt; {
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&lt;Result&gt; handler,
072 *             IntermediateResponseHandler intermediateResponseHandler)
073 *             throws UnsupportedOperationException {
074 *         // ...
075 *     }
076 *
077 *     // ...
078 *
079 * }
080 *
081 * class MyServer implements ServerConnectionFactory&lt;LDAPClientContext, RequestContext&gt; {
082 *     public ServerConnection&lt;RequestContext&gt; accept(LDAPClientContext context) {
083 *         System.out.println(&quot;Connection from: &quot; + 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}