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 2010 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2015 ForgeRock AS
026 */
027package org.forgerock.opendj.grizzly;
028
029import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
030import static org.forgerock.opendj.ldap.LDAPListener.*;
031
032import java.io.IOException;
033import java.net.InetSocketAddress;
034import java.util.concurrent.atomic.AtomicBoolean;
035
036import org.forgerock.i18n.LocalizableMessage;
037import org.forgerock.i18n.slf4j.LocalizedLogger;
038import org.forgerock.opendj.ldap.Connections;
039import org.forgerock.opendj.ldap.LDAPClientContext;
040import org.forgerock.opendj.ldap.ServerConnectionFactory;
041import org.forgerock.opendj.ldap.spi.LDAPListenerImpl;
042import org.forgerock.util.Options;
043import org.glassfish.grizzly.filterchain.FilterChain;
044import org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler;
045import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection;
046import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
047
048import com.forgerock.opendj.util.ReferenceCountedObject;
049
050/**
051 * LDAP listener implementation using Grizzly for transport.
052 */
053public final class GrizzlyLDAPListener implements LDAPListenerImpl {
054    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
055    private final ReferenceCountedObject<TCPNIOTransport>.Reference transport;
056    private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
057    private final TCPNIOServerConnection serverConnection;
058    private final AtomicBoolean isClosed = new AtomicBoolean();
059    private final InetSocketAddress socketAddress;
060    private final Options options;
061
062    /**
063     * Creates a new LDAP listener implementation which will listen for LDAP
064     * client connections using the provided address and connection options.
065     *
066     * @param address
067     *            The address to listen on.
068     * @param factory
069     *            The server connection factory which will be used to create
070     *            server connections.
071     * @param options
072     *            The LDAP listener options.
073     * @throws IOException
074     *             If an error occurred while trying to listen on the provided
075     *             address.
076     */
077    public GrizzlyLDAPListener(final InetSocketAddress address,
078            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
079            final Options options) throws IOException {
080        this(address, factory, options, null);
081    }
082
083    /**
084     * Creates a new LDAP listener implementation which will listen for LDAP
085     * client connections using the provided address, connection options and
086     * provided TCP transport.
087     *
088     * @param address
089     *            The address to listen on.
090     * @param factory
091     *            The server connection factory which will be used to create
092     *            server connections.
093     * @param options
094     *            The LDAP listener options.
095     * @param transport
096     *            Grizzly TCP Transport NIO implementation to use for
097     *            connections. If {@code null}, default transport will be used.
098     * @throws IOException
099     *             If an error occurred while trying to listen on the provided
100     *             address.
101     */
102    public GrizzlyLDAPListener(final InetSocketAddress address,
103            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
104            final Options options, TCPNIOTransport transport) throws IOException {
105        this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport);
106        this.connectionFactory = factory;
107        this.options = Options.copyOf(options);
108        final LDAPServerFilter serverFilter =
109                new LDAPServerFilter(this, options.get(LDAP_DECODE_OPTIONS), options.get(REQUEST_MAX_SIZE_IN_BYTES));
110        final FilterChain ldapChain =
111                GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), serverFilter);
112        final TCPNIOBindingHandler bindingHandler =
113                TCPNIOBindingHandler.builder(this.transport.get()).processor(ldapChain).build();
114        this.serverConnection = bindingHandler.bind(address, options.get(CONNECT_MAX_BACKLOG));
115
116        /*
117         * Get the socket address now, ensuring that the host is the same as the
118         * one provided in the constructor. The port will have changed if 0 was
119         * passed in.
120         */
121        final int port = ((InetSocketAddress) serverConnection.getLocalAddress()).getPort();
122        socketAddress = new InetSocketAddress(Connections.getHostString(address), port);
123    }
124
125    @Override
126    public void close() {
127        if (isClosed.compareAndSet(false, true)) {
128            try {
129                serverConnection.close().get();
130            } catch (final InterruptedException e) {
131                // Cannot handle here.
132                Thread.currentThread().interrupt();
133            } catch (final Exception e) {
134                // TODO: I18N
135                logger.warn(LocalizableMessage.raw("Exception occurred while closing listener", e));
136            }
137            transport.release();
138        }
139    }
140
141    @Override
142    public InetSocketAddress getSocketAddress() {
143        return socketAddress;
144    }
145
146    @Override
147    public String toString() {
148        final StringBuilder builder = new StringBuilder();
149        builder.append("LDAPListener(");
150        builder.append(getSocketAddress());
151        builder.append(')');
152        return builder.toString();
153    }
154
155    ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory() {
156        return connectionFactory;
157    }
158
159    Options getLDAPListenerOptions() {
160        return options;
161    }
162}