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-2014 ForgeRock AS
026 */
027package org.forgerock.opendj.grizzly;
028
029import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
030
031import java.io.IOException;
032import java.net.InetSocketAddress;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.forgerock.opendj.ldap.Connections;
038import org.forgerock.opendj.ldap.LDAPClientContext;
039import org.forgerock.opendj.ldap.LDAPListenerOptions;
040import org.forgerock.opendj.ldap.ServerConnectionFactory;
041import org.forgerock.opendj.ldap.spi.LDAPListenerImpl;
042import org.glassfish.grizzly.filterchain.FilterChain;
043import org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler;
044import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection;
045import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
046
047import com.forgerock.opendj.util.ReferenceCountedObject;
048
049/**
050 * LDAP listener implementation using Grizzly for transport.
051 */
052public final class GrizzlyLDAPListener implements LDAPListenerImpl {
053    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
054    private final ReferenceCountedObject<TCPNIOTransport>.Reference transport;
055    private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
056    private final TCPNIOServerConnection serverConnection;
057    private final AtomicBoolean isClosed = new AtomicBoolean();
058    private final InetSocketAddress socketAddress;
059    private final LDAPListenerOptions options;
060
061    /**
062     * Creates a new LDAP listener implementation which will listen for LDAP
063     * client connections using the provided address and connection options.
064     *
065     * @param address
066     *            The address to listen on.
067     * @param factory
068     *            The server connection factory which will be used to create
069     *            server connections.
070     * @param options
071     *            The LDAP listener options.
072     * @throws IOException
073     *             If an error occurred while trying to listen on the provided
074     *             address.
075     */
076    public GrizzlyLDAPListener(final InetSocketAddress address,
077            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
078            final LDAPListenerOptions options) throws IOException {
079        this(address, factory, options, null);
080    }
081
082    /**
083     * Creates a new LDAP listener implementation which will listen for LDAP
084     * client connections using the provided address, connection options and
085     * provided TCP transport.
086     *
087     * @param address
088     *            The address to listen on.
089     * @param factory
090     *            The server connection factory which will be used to create
091     *            server connections.
092     * @param options
093     *            The LDAP listener options.
094     * @param transport
095     *            Grizzly TCP Transport NIO implementation to use for
096     *            connections. If {@code null}, default transport will be used.
097     * @throws IOException
098     *             If an error occurred while trying to listen on the provided
099     *             address.
100     */
101    public GrizzlyLDAPListener(final InetSocketAddress address,
102            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
103            final LDAPListenerOptions options, TCPNIOTransport transport) throws IOException {
104        this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport);
105        this.connectionFactory = factory;
106        this.options = new LDAPListenerOptions(options);
107        final LDAPServerFilter serverFilter =
108                new LDAPServerFilter(this, this.options.getDecodeOptions(), this.options
109                        .getMaxRequestSize());
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.getBacklog());
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    LDAPListenerOptions getLDAPListenerOptions() {
160        return options;
161    }
162}