From 5bb6c450fbf04361e37a0fc1de2a7220de00e8ee Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Fri, 18 Oct 2013 15:52:51 +0000
Subject: [PATCH] Fix OPENDJ-41: Expose LDAP Grizzly filter APIs This a a part of OPENDJ-175 - Decouple OpenDJ LDAP SDK from Grizzly  CR2491

---
 opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java        |   21 ---
 opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java          |   16 --
 opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java                 |  126 +++++++++++++++++++++
 opendj-sdk/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyUtilsTestCase.java         |  113 ++++++++++++++++++
 opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java |    5 
 opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java             |   21 ---
 6 files changed, 247 insertions(+), 55 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
index 40cd7b9..6a2f5d8 100644
--- a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
@@ -84,7 +84,6 @@
 import org.glassfish.grizzly.CompletionHandler;
 import org.glassfish.grizzly.filterchain.Filter;
 import org.glassfish.grizzly.filterchain.FilterChain;
-import org.glassfish.grizzly.filterchain.FilterChainBuilder;
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
 import org.glassfish.grizzly.ssl.SSLFilter;
 
@@ -706,25 +705,7 @@
      */
     void installFilter(final Filter filter) {
         synchronized (stateLock) {
-            // Determine the index where the filter should be added.
-            final FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
-            int filterIndex = oldFilterChain.size() - 1;
-            if (filter instanceof SSLFilter) {
-                // Beneath any ConnectionSecurityLayerFilters if present,
-                // otherwise beneath the LDAP filter.
-                for (int i = oldFilterChain.size() - 2; i >= 0; i--) {
-                    if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter)) {
-                        filterIndex = i + 1;
-                        break;
-                    }
-                }
-            }
-
-            // Create the new filter chain.
-            final FilterChain newFilterChain =
-                    FilterChainBuilder.stateless().addAll(oldFilterChain).add(filterIndex, filter)
-                            .build();
-            connection.setProcessor(newFilterChain);
+            GrizzlyUtils.addFilterToConnection(filter, connection);
         }
     }
 
diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
index 10e10e0..165e400 100644
--- a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
@@ -53,8 +53,6 @@
 import org.glassfish.grizzly.EmptyCompletionHandler;
 import org.glassfish.grizzly.SocketConnectorHandler;
 import org.glassfish.grizzly.filterchain.FilterChain;
-import org.glassfish.grizzly.filterchain.FilterChainBuilder;
-import org.glassfish.grizzly.filterchain.TransportFilter;
 import org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler;
 import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
 
@@ -257,7 +255,8 @@
         this.socketAddress = address;
         this.options = new LDAPOptions(options);
         this.clientFilter = new LDAPClientFilter(new LDAPReader(this.options.getDecodeOptions()), 0);
-        this.defaultFilterChain = FilterChainBuilder.stateless().add(new TransportFilter()).add(clientFilter).build();
+        this.defaultFilterChain = GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), clientFilter);
+
     }
 
     @Override
diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java
index bcc0f88..b441b64 100644
--- a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java
@@ -40,8 +40,6 @@
 import org.forgerock.opendj.ldap.ServerConnectionFactory;
 import org.forgerock.opendj.ldap.spi.LDAPListenerImpl;
 import org.glassfish.grizzly.filterchain.FilterChain;
-import org.glassfish.grizzly.filterchain.FilterChainBuilder;
-import org.glassfish.grizzly.filterchain.TransportFilter;
 import org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler;
 import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection;
 import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
@@ -53,7 +51,6 @@
  */
 public final class GrizzlyLDAPListener implements LDAPListenerImpl {
     private final ReferenceCountedObject<TCPNIOTransport>.Reference transport;
-    private final FilterChain defaultFilterChain;
     private final ServerConnectionFactory<LDAPClientContext, Integer> connectionFactory;
     private final TCPNIOServerConnection serverConnection;
     private final AtomicBoolean isClosed = new AtomicBoolean();
@@ -105,12 +102,11 @@
         this.connectionFactory = factory;
 
         final DecodeOptions decodeOptions = new DecodeOptions(options.getDecodeOptions());
-        this.defaultFilterChain =
-                FilterChainBuilder.stateless().add(new TransportFilter()).add(
-                        new LDAPServerFilter(this, new LDAPReader(decodeOptions), options
-                                .getMaxRequestSize())).build();
+        final LDAPServerFilter serverFilter = new LDAPServerFilter(this, new LDAPReader(decodeOptions), options
+                .getMaxRequestSize());
+        final FilterChain ldapChain = GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), serverFilter);
         final TCPNIOBindingHandler bindingHandler =
-                TCPNIOBindingHandler.builder(this.transport.get()).processor(defaultFilterChain).build();
+                TCPNIOBindingHandler.builder(this.transport.get()).processor(ldapChain).build();
         this.serverConnection = bindingHandler.bind(address, options.getBacklog());
     }
 
@@ -148,8 +144,4 @@
     ServerConnectionFactory<LDAPClientContext, Integer> getConnectionFactory() {
         return connectionFactory;
     }
-
-    FilterChain getDefaultFilterChain() {
-        return defaultFilterChain;
-    }
 }
diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
new file mode 100644
index 0000000..4750bdd
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
@@ -0,0 +1,126 @@
+/*
+ * 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 com.forgerock.opendj.grizzly;
+
+import org.glassfish.grizzly.Processor;
+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.ssl.SSLFilter;
+
+/**
+ * Common utility methods.
+ */
+final class GrizzlyUtils {
+
+
+    /**
+     * Build a filter chain from the provided processor if possible and the
+     * provided filter.
+     * <p>
+     * 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
+     */
+    public 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.
+     * <p>
+     * For a non-SSL filter, filter is added at the last position before the
+     * LDAP filter.
+     * <p>
+     * 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
+     */
+    public static void addFilterToConnection(final Filter filter, org.glassfish.grizzly.Connection<?> connection) {
+        final FilterChain currentChain = (FilterChain) connection.getProcessor();
+        final FilterChain newChain = addFilterToChain(filter, currentChain);
+        connection.setProcessor(newChain);
+    }
+
+    /**
+     * Adds a filter to a filter chain.
+     * <p>
+     * For a non-SSL filter, filter is added at the last position before the
+     * LDAP filter.
+     * <p>
+     * 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
+     */
+    public 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();
+    }
+
+    // Prevent instantiation.
+    private GrizzlyUtils() {
+        // No implementation required.
+    }
+
+}
diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
index 6080156..f0c9f43 100644
--- a/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -76,7 +76,6 @@
 import org.glassfish.grizzly.filterchain.BaseFilter;
 import org.glassfish.grizzly.filterchain.Filter;
 import org.glassfish.grizzly.filterchain.FilterChain;
-import org.glassfish.grizzly.filterchain.FilterChainBuilder;
 import org.glassfish.grizzly.filterchain.FilterChainContext;
 import org.glassfish.grizzly.filterchain.NextAction;
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
@@ -360,25 +359,7 @@
          *            The filter to be installed.
          */
         private void installFilter(final Filter filter) {
-            // Determine the index where the filter should be added.
-            final FilterChain oldFilterChain = (FilterChain) connection.getProcessor();
-            int filterIndex = oldFilterChain.size() - 1;
-            if (filter instanceof SSLFilter) {
-                // Beneath any ConnectionSecurityLayerFilters if present,
-                // otherwise beneath the LDAP filter.
-                for (int i = oldFilterChain.size() - 2; i >= 0; i--) {
-                    if (!(oldFilterChain.get(i) instanceof ConnectionSecurityLayerFilter)) {
-                        filterIndex = i + 1;
-                        break;
-                    }
-                }
-            }
-
-            // Create the new filter chain.
-            final FilterChain newFilterChain =
-                    FilterChainBuilder.stateless().addAll(oldFilterChain).add(filterIndex, filter)
-                            .build();
-            connection.setProcessor(newFilterChain);
+            GrizzlyUtils.addFilterToConnection(filter, connection);
         }
 
         /**
diff --git a/opendj-sdk/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyUtilsTestCase.java b/opendj-sdk/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyUtilsTestCase.java
new file mode 100644
index 0000000..845e0fe
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyUtilsTestCase.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.forgerock.opendj.grizzly;
+
+import static org.fest.assertions.Assertions.*;
+
+import org.forgerock.opendj.ldap.SdkTestCase;
+import org.glassfish.grizzly.StandaloneProcessor;
+import org.glassfish.grizzly.filterchain.BaseFilter;
+import org.glassfish.grizzly.filterchain.FilterChain;
+import org.glassfish.grizzly.filterchain.FilterChainBuilder;
+import org.glassfish.grizzly.filterchain.TransportFilter;
+import org.glassfish.grizzly.ssl.SSLFilter;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class GrizzlyUtilsTestCase extends SdkTestCase {
+
+    private static final class DummyLDAPFilter extends BaseFilter { // only need type
+    }
+
+    private static final class FilterOne extends BaseFilter { // only need type
+    }
+
+    private static final class DummySSLFilter extends SSLFilter { // only need type
+    }
+
+    /**
+     * Default filter chain contains a transport filter and a ldap filter.
+     */
+    private FilterChain getDefaultFilterChain() {
+        return FilterChainBuilder.stateless().
+                add(new TransportFilter()).add(new DummyLDAPFilter()).build();
+    }
+
+    @Test
+    public void addFilterToChain() throws Exception {
+        final FilterChain chain = GrizzlyUtils.addFilterToChain(new FilterOne(), getDefaultFilterChain());
+
+        assertThat(chain.indexOfType(TransportFilter.class)).isEqualTo(0);
+        assertThat(chain.indexOfType(FilterOne.class)).isEqualTo(1);
+        assertThat(chain.indexOfType(DummyLDAPFilter.class)).isEqualTo(2);
+        assertThat(chain.size()).isEqualTo(3);
+    }
+
+    @Test
+    public void addSSLFilterToChain() throws Exception {
+        final FilterChain chain = GrizzlyUtils.addFilterToChain(new DummySSLFilter(), getDefaultFilterChain());
+
+        assertThat(chain.indexOfType(TransportFilter.class)).isEqualTo(0);
+        assertThat(chain.indexOfType(DummySSLFilter.class)).isEqualTo(1);
+        assertThat(chain.indexOfType(DummyLDAPFilter.class)).isEqualTo(2);
+        assertThat(chain.size()).isEqualTo(3);
+    }
+
+    @Test
+    public void addConnectionSecurityLayerAndSSLFilterToChain() throws Exception {
+        final FilterChain chain = GrizzlyUtils.addFilterToChain(new ConnectionSecurityLayerFilter(null, null),
+                getDefaultFilterChain());
+        final FilterChain sslChain = GrizzlyUtils.addFilterToChain(new DummySSLFilter(), chain);
+
+        // SSLFilter must be beneath ConnectionSecurityLayerFilter
+        assertThat(sslChain.indexOfType(TransportFilter.class)).isEqualTo(0);
+        assertThat(sslChain.indexOfType(DummySSLFilter.class)).isEqualTo(1);
+        assertThat(sslChain.indexOfType(ConnectionSecurityLayerFilter.class)).isEqualTo(2);
+        assertThat(sslChain.indexOfType(DummyLDAPFilter.class)).isEqualTo(3);
+        assertThat(sslChain.size()).isEqualTo(4);
+    }
+
+    @Test
+    public void buildFilterChainFromFilterChainProcessor() throws Exception {
+        final FilterChain chain = GrizzlyUtils.buildFilterChain(
+                FilterChainBuilder.stateless().add(new TransportFilter()).build(), new DummyLDAPFilter());
+
+        assertThat(chain.indexOfType(TransportFilter.class)).isEqualTo(0);
+        assertThat(chain.indexOfType(DummyLDAPFilter.class)).isEqualTo(1);
+        assertThat(chain.size()).isEqualTo(2);
+    }
+
+    @Test
+    public void buildFilterChainFromNonFilterChainProcessor() throws Exception {
+        final FilterChain chain = GrizzlyUtils.buildFilterChain(new StandaloneProcessor(), new DummyLDAPFilter());
+
+        assertThat(chain.indexOfType(TransportFilter.class)).isEqualTo(0);
+        assertThat(chain.indexOfType(DummyLDAPFilter.class)).isEqualTo(1);
+        assertThat(chain.size()).isEqualTo(2);
+    }
+
+
+}

--
Gitblit v1.10.0