001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010 Sun Microsystems, Inc.
015 * Portions copyright 2011-2015 ForgeRock AS.
016 */
017package org.forgerock.opendj.grizzly;
018
019import static org.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
020import static org.forgerock.opendj.ldap.LDAPListener.*;
021
022import java.io.IOException;
023import java.net.InetSocketAddress;
024import java.util.concurrent.atomic.AtomicBoolean;
025
026import org.forgerock.i18n.LocalizableMessage;
027import org.forgerock.i18n.slf4j.LocalizedLogger;
028import org.forgerock.opendj.ldap.Connections;
029import org.forgerock.opendj.ldap.LDAPClientContext;
030import org.forgerock.opendj.ldap.ServerConnectionFactory;
031import org.forgerock.opendj.ldap.spi.LDAPListenerImpl;
032import org.forgerock.util.Options;
033import org.glassfish.grizzly.filterchain.FilterChain;
034import org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler;
035import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection;
036import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
037
038import com.forgerock.opendj.util.ReferenceCountedObject;
039
040/**
041 * LDAP listener implementation using Grizzly for transport.
042 */
043public final class GrizzlyLDAPListener implements LDAPListenerImpl {
044    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
045    private final ReferenceCountedObject<TCPNIOTransport>.Reference transport;
046    private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
047    private final TCPNIOServerConnection serverConnection;
048    private final AtomicBoolean isClosed = new AtomicBoolean();
049    private final InetSocketAddress socketAddress;
050    private final Options options;
051
052    /**
053     * Creates a new LDAP listener implementation which will listen for LDAP
054     * client connections using the provided address and connection options.
055     *
056     * @param address
057     *            The address to listen on.
058     * @param factory
059     *            The server connection factory which will be used to create
060     *            server connections.
061     * @param options
062     *            The LDAP listener options.
063     * @throws IOException
064     *             If an error occurred while trying to listen on the provided
065     *             address.
066     */
067    public GrizzlyLDAPListener(final InetSocketAddress address,
068            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
069            final Options options) throws IOException {
070        this(address, factory, options, null);
071    }
072
073    /**
074     * Creates a new LDAP listener implementation which will listen for LDAP
075     * client connections using the provided address, connection options and
076     * provided TCP transport.
077     *
078     * @param address
079     *            The address to listen on.
080     * @param factory
081     *            The server connection factory which will be used to create
082     *            server connections.
083     * @param options
084     *            The LDAP listener options.
085     * @param transport
086     *            Grizzly TCP Transport NIO implementation to use for
087     *            connections. If {@code null}, default transport will be used.
088     * @throws IOException
089     *             If an error occurred while trying to listen on the provided
090     *             address.
091     */
092    public GrizzlyLDAPListener(final InetSocketAddress address,
093            final ServerConnectionFactory<LDAPClientContext, Integer> factory,
094            final Options options, TCPNIOTransport transport) throws IOException {
095        this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport);
096        this.connectionFactory = factory;
097        this.options = Options.copyOf(options);
098        final LDAPServerFilter serverFilter =
099                new LDAPServerFilter(this, options.get(LDAP_DECODE_OPTIONS), options.get(REQUEST_MAX_SIZE_IN_BYTES));
100        final FilterChain ldapChain =
101                GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), serverFilter);
102        final TCPNIOBindingHandler bindingHandler =
103                TCPNIOBindingHandler.builder(this.transport.get()).processor(ldapChain).build();
104        this.serverConnection = bindingHandler.bind(address, options.get(CONNECT_MAX_BACKLOG));
105
106        /*
107         * Get the socket address now, ensuring that the host is the same as the
108         * one provided in the constructor. The port will have changed if 0 was
109         * passed in.
110         */
111        final int port = ((InetSocketAddress) serverConnection.getLocalAddress()).getPort();
112        socketAddress = new InetSocketAddress(Connections.getHostString(address), port);
113    }
114
115    @Override
116    public void close() {
117        if (isClosed.compareAndSet(false, true)) {
118            try {
119                serverConnection.close().get();
120            } catch (final InterruptedException e) {
121                // Cannot handle here.
122                Thread.currentThread().interrupt();
123            } catch (final Exception e) {
124                // TODO: I18N
125                logger.warn(LocalizableMessage.raw("Exception occurred while closing listener", e));
126            }
127            transport.release();
128        }
129    }
130
131    @Override
132    public InetSocketAddress getSocketAddress() {
133        return socketAddress;
134    }
135
136    @Override
137    public String toString() {
138        final StringBuilder builder = new StringBuilder();
139        builder.append("LDAPListener(");
140        builder.append(getSocketAddress());
141        builder.append(')');
142        return builder.toString();
143    }
144
145    ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory() {
146        return connectionFactory;
147    }
148
149    Options getLDAPListenerOptions() {
150        return options;
151    }
152}