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-2014 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.Reject;
040
041/**
042 * An LDAP server connection listener which waits for LDAP connection requests
043 * to come in over the network and binds them to a {@link ServerConnection}
044 * created using the provided {@link ServerConnectionFactory}.
045 * <p>
046 * When processing requests, {@code ServerConnection} implementations are passed
047 * an integer as the first parameter. This integer represents the
048 * {@code requestID} associated with the client request and corresponds to the
049 * {@code requestID} passed as a parameter to abandon and cancel extended
050 * requests. The request ID may also be useful for logging purposes.
051 * <p>
052 * An {@code LDAPListener} does not require {@code ServerConnection}
053 * implementations to return a result when processing requests. More
054 * specifically, an {@code LDAPListener} does not maintain any internal state
055 * information associated with each request which must be released. This is
056 * useful when implementing LDAP abandon operations which may prevent results
057 * being sent for abandoned operations.
058 * <p>
059 * The following code illustrates how to create a simple LDAP server:
060 *
061 * <pre>
062 * class MyClientConnection implements ServerConnection&lt;Integer&gt; {
063 *     private final LDAPClientContext clientContext;
064 *
065 *     private MyClientConnection(LDAPClientContext clientContext) {
066 *         this.clientContext = clientContext;
067 *     }
068 *
069 *     public void add(Integer requestID, AddRequest request, ResultHandler&lt;Result&gt; handler,
070 *             IntermediateResponseHandler intermediateResponseHandler)
071 *             throws UnsupportedOperationException {
072 *         // ...
073 *     }
074 *
075 *     // ...
076 *
077 * }
078 *
079 * class MyServer implements ServerConnectionFactory&lt;LDAPClientContext, RequestContext&gt; {
080 *     public ServerConnection&lt;RequestContext&gt; accept(LDAPClientContext context) {
081 *         System.out.println(&quot;Connection from: &quot; + context.getPeerAddress());
082 *         return new MyClientConnection(context);
083 *     }
084 * }
085 *
086 * public static void main(String[] args) throws Exception {
087 *     LDAPListener listener = new LDAPListener(1389, new MyServer());
088 *
089 *     // ...
090 *
091 *     listener.close();
092 * }
093 * </pre>
094 */
095public final class LDAPListener implements Closeable {
096
097    /**
098     * We implement the factory using the pimpl idiom in order have
099     * cleaner Javadoc which does not expose implementation methods.
100     */
101    private final LDAPListenerImpl impl;
102
103    /**
104     * Transport provider that provides the implementation of this listener.
105     */
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, new LDAPListenerOptions());
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 LDAPListenerOptions options) throws IOException {
148        Reject.ifNull(factory, options);
149        final InetSocketAddress address = new InetSocketAddress(port);
150        this.provider = getProvider(TransportProvider.class, options.getTransportProvider(),
151                options.getProviderClassLoader());
152        this.impl = provider.getLDAPListener(address, factory, options);
153    }
154
155    /**
156     * Creates a new LDAP listener implementation which will listen for LDAP
157     * client connections at the provided address.
158     *
159     * @param address
160     *            The address to listen on.
161     * @param factory
162     *            The server connection factory which will be used to create
163     *            server connections.
164     * @throws IOException
165     *             If an error occurred while trying to listen on the provided
166     *             address.
167     * @throws NullPointerException
168     *             If {@code address} or {code factory} was {@code null}.
169     */
170    public LDAPListener(final InetSocketAddress address,
171            final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException {
172        this(address, factory, new LDAPListenerOptions());
173    }
174
175    /**
176     * Creates a new LDAP listener implementation which will listen for LDAP
177     * client connections at the provided address.
178     *
179     * @param address
180     *            The address to listen on.
181     * @param factory
182     *            The server connection factory which will be used to create
183     *            server connections.
184     * @param options
185     *            The LDAP listener options.
186     * @throws IOException
187     *             If an error occurred while trying to listen on the provided
188     *             address.
189     * @throws NullPointerException
190     *             If {@code address}, {code factory}, or {@code options} was
191     *             {@code null}.
192     */
193    public LDAPListener(final InetSocketAddress address,
194            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
195            final LDAPListenerOptions options) throws IOException {
196        Reject.ifNull(address, factory, options);
197        this.provider = getProvider(TransportProvider.class, options.getTransportProvider(),
198                options.getProviderClassLoader());
199        this.impl = provider.getLDAPListener(address, factory, options);
200    }
201
202    /**
203     * Creates a new LDAP listener implementation which will listen for LDAP
204     * client connections at the provided address.
205     *
206     * @param host
207     *            The address to listen on.
208     * @param port
209     *            The port to listen on.
210     * @param factory
211     *            The server connection factory which will be used to create
212     *            server connections.
213     * @throws IOException
214     *             If an error occurred while trying to listen on the provided
215     *             address.
216     * @throws NullPointerException
217     *             If {@code host} or {code factory} was {@code null}.
218     */
219    public LDAPListener(final String host, final int port,
220            final ServerConnectionFactory<LDAPClientContext, Integer> factory) throws IOException {
221        this(host, port, factory, new LDAPListenerOptions());
222    }
223
224    /**
225     * Creates a new LDAP listener implementation which will listen for LDAP
226     * client connections at the provided address.
227     *
228     * @param host
229     *            The address to listen on.
230     * @param port
231     *            The port to listen on.
232     * @param factory
233     *            The server connection factory which will be used to create
234     *            server connections.
235     * @param options
236     *            The LDAP listener options.
237     * @throws IOException
238     *             If an error occurred while trying to listen on the provided
239     *             address.
240     * @throws NullPointerException
241     *             If {@code host}, {code factory}, or {@code options} was
242     *             {@code null}.
243     */
244    public LDAPListener(final String host, final int port,
245            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
246            final LDAPListenerOptions options) throws IOException {
247        Reject.ifNull(host, factory, options);
248        final InetSocketAddress address = new InetSocketAddress(host, port);
249        this.provider = getProvider(TransportProvider.class, options.getTransportProvider(),
250                options.getProviderClassLoader());
251        this.impl = provider.getLDAPListener(address, factory, options);
252    }
253
254    /**
255     * Closes this LDAP connection listener.
256     */
257    @Override
258    public void close() {
259        impl.close();
260    }
261
262    /**
263     * Returns the {@code InetAddress} that this LDAP listener is listening on.
264     *
265     * @return The {@code InetAddress} that this LDAP listener is listening on.
266     */
267    public InetAddress getAddress() {
268        return getSocketAddress().getAddress();
269    }
270
271    /**
272     * Returns the host name that this LDAP listener is listening on. The
273     * returned host name is the same host name that was provided during
274     * construction and may be an IP address. More specifically, this method
275     * will not perform a reverse DNS lookup.
276     *
277     * @return The host name that this LDAP listener is listening on.
278     */
279    public String getHostName() {
280        return Connections.getHostString(getSocketAddress());
281    }
282
283    /**
284     * Returns the port that this LDAP listener is listening on.
285     *
286     * @return The port that this LDAP listener is listening on.
287     */
288    public int getPort() {
289        return getSocketAddress().getPort();
290    }
291
292    /**
293     * Returns the address that this LDAP listener is listening on.
294     *
295     * @return The address that this LDAP listener is listening on.
296     */
297    public InetSocketAddress getSocketAddress() {
298        return impl.getSocketAddress();
299    }
300
301    /**
302     * Returns the name of the transport provider, which provides the implementation
303     * of this factory.
304     *
305     * @return The name of actual transport provider.
306     */
307    public String getProviderName() {
308        return provider.getName();
309    }
310
311    @Override
312    public String toString() {
313        return impl.toString();
314    }
315
316}