/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2013 ForgeRock AS. */ package org.forgerock.opendj.grizzly; import java.net.Socket; import java.net.SocketException; import java.nio.channels.SocketChannel; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.io.LDAP; import org.forgerock.opendj.io.LDAPReader; import org.forgerock.opendj.io.LDAPWriter; import org.forgerock.opendj.ldap.DecodeOptions; import org.glassfish.grizzly.Connection; import org.glassfish.grizzly.Processor; import org.glassfish.grizzly.ThreadCache; import org.glassfish.grizzly.filterchain.Filter; import org.glassfish.grizzly.filterchain.FilterChain; import org.glassfish.grizzly.filterchain.FilterChainBuilder; import org.glassfish.grizzly.filterchain.TransportFilter; import org.glassfish.grizzly.memory.MemoryManager; import org.glassfish.grizzly.nio.transport.TCPNIOConnection; import org.glassfish.grizzly.ssl.SSLFilter; /** * Common utility methods. */ final class GrizzlyUtils { @SuppressWarnings("rawtypes") private static final ThreadCache.CachedTypeIndex WRITER_INDEX = ThreadCache .obtainIndex(LDAPWriter.class, 1); /** * Build a filter chain from the provided processor if possible and the * provided filter. *

* If the provided processor can't be used for building the new filter * chain, then a chain with only a {@code TransportFilter} is used as a base * chain. * * @param processor * processor to build the filter chain from. If the processor is * not a filter chain (for example, it can be a * {@code StandaloneProcessor} then it is ignored to build the * returned filter chain * @param filter * filter to add at the end of the filter chain * @return a new filter chain, based on the provided processor if processor * is a {@code FilterChain}, and having the provided filter as the * last filter */ static FilterChain buildFilterChain(Processor processor, Filter filter) { if (processor instanceof FilterChain) { return FilterChainBuilder.stateless().addAll((FilterChain) processor).add(filter).build(); } else { return FilterChainBuilder.stateless().add(new TransportFilter()).add(filter).build(); } } /** * Adds a filter to filter chain registered with the given connection. *

* For a non-SSL filter, filter is added at the last position before the * LDAP filter. *

* For a SSL filter, filter is added before any * {@code ConnectionSecurityLayerFilter} which is already present in the * filter chain. * * @param filter * filter to add * @param connection * connection to update with the new filter chain containing the * provided filter */ static void addFilterToConnection(final Filter filter, Connection connection) { final FilterChain currentChain = (FilterChain) connection.getProcessor(); final FilterChain newChain = addFilterToChain(filter, currentChain); connection.setProcessor(newChain); } /** * Adds a filter to a filter chain. *

* For a non-SSL filter, filter is added at the last position before the * LDAP filter. *

* For a SSL filter, filter is added before any * {@code ConnectionSecurityLayerFilter} which is already present in the * filter chain. * * @param filter * filter to add * @param chain * initial filter chain * @return a new filter chain which includes the provided filter */ static FilterChain addFilterToChain(final Filter filter, final FilterChain chain) { // By default, before LDAP filter which is the last one int indexToAddFilter = chain.size() - 1; if (filter instanceof SSLFilter) { // Before any ConnectionSecurityLayerFilters if present for (int i = chain.size() - 2; i >= 0; i--) { if (!(chain.get(i) instanceof ConnectionSecurityLayerFilter)) { indexToAddFilter = i + 1; break; } } } return FilterChainBuilder.stateless().addAll(chain).add(indexToAddFilter, filter).build(); } /** * Creates a new LDAP Reader with the provided maximum size of ASN1 element, * options and memory manager. * * @param decodeOptions * allow to control how responses and requests are decoded * @param maxASN1ElementSize * The maximum BER element size, or 0 to indicate * that there is no limit. * @param memoryManager * The memory manager to use for buffering. * @return a LDAP reader */ static LDAPReader createReader(DecodeOptions decodeOptions, int maxASN1ElementSize, MemoryManager memoryManager) { ASN1BufferReader asn1Reader = new ASN1BufferReader(maxASN1ElementSize, memoryManager); return LDAP.getReader(asn1Reader, decodeOptions); } /** * Returns a LDAP writer, with a clean ASN1Writer, possibly from * the thread local cache. *

* The writer is either returned from thread local cache or created. * In the former case, the writer is removed from the cache. * * @return a LDAP writer */ @SuppressWarnings("unchecked") static LDAPWriter getWriter() { LDAPWriter writer = ThreadCache.takeFromCache(WRITER_INDEX); if (writer == null) { writer = LDAP.getWriter(new ASN1BufferWriter()); } writer.getASN1Writer().reset(); return writer; } /** * Recycle a LDAP writer to a thread local cache. *

* The LDAP writer is then available for the thread using the * {@get()} method. * * @param writer LDAP writer to recycle */ static void recycleWriter(LDAPWriter writer) { writer.getASN1Writer().recycle(); ThreadCache.putToCache(WRITER_INDEX, writer); } static void configureConnection(final Connection connection, final boolean tcpNoDelay, final boolean keepAlive, final boolean reuseAddress, final int linger, final LocalizedLogger logger) { /* * Test shows that its much faster with non block writes but risk * running out of memory if the server is slow. */ connection.configureBlocking(true); // Configure socket options. final SocketChannel channel = (SocketChannel) ((TCPNIOConnection) connection).getChannel(); final Socket socket = channel.socket(); try { socket.setTcpNoDelay(tcpNoDelay); } catch (final SocketException e) { logger.traceException(e, "Unable to set TCP_NODELAY to %d on client connection", tcpNoDelay); } try { socket.setKeepAlive(keepAlive); } catch (final SocketException e) { logger.traceException(e, "Unable to set SO_KEEPALIVE to %d on client connection", keepAlive); } try { socket.setReuseAddress(reuseAddress); } catch (final SocketException e) { logger.traceException(e, "Unable to set SO_REUSEADDR to %d on client connection", reuseAddress); } try { if (linger < 0) { socket.setSoLinger(false, 0); } else { socket.setSoLinger(true, linger); } } catch (final SocketException e) { logger.traceException(e, "Unable to set SO_LINGER to %d on client connection", linger); } } // Prevent instantiation. private GrizzlyUtils() { // No implementation required. } }