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}