FELIX-4860 : Revisit HandlerRegistry implementation. Unify all listener trackers and info objects into a single object. Cleanup registry handling
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1681575 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/EventListenerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/EventListenerRegistry.java
index 132c71f..ea28874 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/EventListenerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/EventListenerRegistry.java
@@ -90,33 +90,35 @@
*/
public void addListeners(@Nonnull final ListenerHandler handler)
{
+ final int reason = handler.init();
+
if ( handler.getListenerInfo().isListenerType(ServletContextListener.class.getName()))
{
- this.contextListeners.add(handler);
+ this.contextListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(ServletContextAttributeListener.class.getName()))
{
- this.contextAttributeListeners.add(handler);
+ this.contextAttributeListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(HttpSessionListener.class.getName()))
{
- this.sessionListeners.add(handler);
+ this.sessionListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(HttpSessionIdListener.class.getName()))
{
- this.sessionIdListeners.add(handler);
+ this.sessionIdListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(HttpSessionAttributeListener.class.getName()))
{
- this.sessionAttributeListeners.add(handler);
+ this.sessionAttributeListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(ServletRequestListener.class.getName()))
{
- this.requestListeners.add(handler);
+ this.requestListeners.add(handler, reason);
}
if ( handler.getListenerInfo().isListenerType(ServletRequestAttributeListener.class.getName()))
{
- this.requestAttributeListeners.add(handler);
+ this.requestAttributeListeners.add(handler, reason);
}
}
@@ -127,33 +129,39 @@
*/
public void removeListeners(@Nonnull final ListenerInfo info)
{
+ // each listener map returns the same handler, we just need it once to destory
+ ListenerHandler handler = null;
if ( info.isListenerType(ServletContextListener.class.getName()))
{
- this.contextListeners.remove(info);
+ handler = this.contextListeners.remove(info);
}
if ( info.isListenerType(ServletContextAttributeListener.class.getName()))
{
- this.contextAttributeListeners.remove(info);
+ handler = this.contextAttributeListeners.remove(info);
}
if ( info.isListenerType(HttpSessionListener.class.getName()))
{
- this.sessionListeners.remove(info);
+ handler = this.sessionListeners.remove(info);
}
if ( info.isListenerType(HttpSessionIdListener.class.getName()))
{
- this.sessionIdListeners.remove(info);
+ handler = this.sessionIdListeners.remove(info);
}
if ( info.isListenerType(HttpSessionAttributeListener.class.getName()))
{
- this.sessionAttributeListeners.remove(info);
+ handler = this.sessionAttributeListeners.remove(info);
}
if ( info.isListenerType(ServletRequestListener.class.getName()))
{
- this.requestListeners.remove(info);
+ handler = this.requestListeners.remove(info);
}
if ( info.isListenerType(ServletRequestAttributeListener.class.getName()))
{
- this.requestAttributeListeners.remove(info);
+ handler = this.requestAttributeListeners.remove(info);
+ }
+ if ( handler != null )
+ {
+ handler.destroy();
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ListenerMap.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ListenerMap.java
index da56b8c..d438ba3 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ListenerMap.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ListenerMap.java
@@ -80,9 +80,8 @@
this.handlers = Collections.emptyList();
}
- public synchronized void add(final ListenerHandler handler)
+ public synchronized void add(final ListenerHandler handler, final int reason)
{
- final int reason = handler.init();
final ListenerRegistrationStatus<T> status = new ListenerRegistrationStatus<T>(handler, reason);
final List<ListenerRegistrationStatus<T>> newList = new ArrayList<ListenerMap.ListenerRegistrationStatus<T>>(this.handlers);
@@ -91,7 +90,7 @@
this.handlers = newList;
}
- public synchronized void remove(final ListenerInfo info)
+ public synchronized ListenerHandler remove(final ListenerInfo info)
{
final List<ListenerRegistrationStatus<T>> newList = new ArrayList<ListenerMap.ListenerRegistrationStatus<T>>(this.handlers);
final Iterator<ListenerRegistrationStatus<T>> i = newList.iterator();
@@ -100,15 +99,13 @@
final ListenerRegistrationStatus<T> status = i.next();
if ( status.getHandler().getListenerInfo().equals(info) )
{
- if ( status.getResult() == - 1 )
- {
- status.getHandler().destroy();
- }
i.remove();
this.handlers = newList;
- break;
+
+ return status.getResult() == -1 ? status.getHandler() : null;
}
}
+ return null;
}
public ListenerHandler getListenerHandler(@Nonnull final ListenerInfo info)
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/registry/EventListenerRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/EventListenerRegistryTest.java
new file mode 100644
index 0000000..3a00baf
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/EventListenerRegistryTest.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.HttpServiceFilterHandler;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.dto.FailedDTOHolder;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class EventListenerRegistryTest {
+
+ private final FilterRegistry reg = new FilterRegistry();
+
+ private void assertEmpty(final ServletContextDTO dto, final FailedDTOHolder holder)
+ {
+ assertNull(dto.filterDTOs);
+ assertTrue(holder.failedFilterDTOs.isEmpty());
+ }
+
+ private void clear(final ServletContextDTO dto, final FailedDTOHolder holder)
+ {
+ dto.filterDTOs = null;
+ holder.failedFilterDTOs.clear();
+ }
+
+ @Test public void testSingleFilter() throws InvalidSyntaxException, ServletException
+ {
+ final FailedDTOHolder holder = new FailedDTOHolder();
+ final ServletContextDTO dto = new ServletContextDTO();
+
+ // check DTO
+ reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
+ assertEmpty(dto, holder);
+
+ // register filter
+ final FilterHandler h1 = createFilterHandler(1L, 0, "/foo");
+ reg.addFilter(h1);
+
+ verify(h1.getFilter()).init(Matchers.any(FilterConfig.class));
+
+ // one entry in DTO
+ clear(dto, holder);
+ reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
+ assertTrue(holder.failedFilterDTOs.isEmpty());
+ assertNotNull(dto.filterDTOs);
+ assertEquals(1, dto.filterDTOs.length);
+ assertEquals(1, dto.filterDTOs[0].patterns.length);
+ assertEquals("/foo", dto.filterDTOs[0].patterns[0]);
+
+ // remove filter
+ final Filter f = h1.getFilter();
+ reg.removeFilter(h1.getFilterInfo(), true);
+ verify(f).destroy();
+
+ // empty again
+ clear(dto, holder);
+ reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
+ assertEmpty(dto, holder);
+ }
+
+ @Test public void testFilterOrdering() throws InvalidSyntaxException
+ {
+ final FilterHandler h1 = createFilterHandler(1L, 20, "/foo");
+ reg.addFilter(h1);
+ final FilterHandler h2 = createFilterHandler(2L, 10, "/foo");
+ reg.addFilter(h2);
+ final FilterHandler h3 = createFilterHandler(3L, 30, "/foo");
+ reg.addFilter(h3);
+ final FilterHandler h4 = createFilterHandler(4L, 0, "/other");
+ reg.addFilter(h4);
+ final FilterHandler h5 = createFilterHandler(5L, 90, "/foo");
+ reg.addFilter(h5);
+
+ final FilterHandler[] handlers = reg.getFilterHandlers(null, DispatcherType.REQUEST, "/foo");
+ assertEquals(4, handlers.length);
+ assertEquals(h5.getFilterInfo(), handlers[0].getFilterInfo());
+ assertEquals(h3.getFilterInfo(), handlers[1].getFilterInfo());
+ assertEquals(h1.getFilterInfo(), handlers[2].getFilterInfo());
+ assertEquals(h2.getFilterInfo(), handlers[3].getFilterInfo());
+
+ // cleanup
+ reg.removeFilter(h1.getFilterInfo(), true);
+ reg.removeFilter(h2.getFilterInfo(), true);
+ reg.removeFilter(h3.getFilterInfo(), true);
+ reg.removeFilter(h4.getFilterInfo(), true);
+ reg.removeFilter(h5.getFilterInfo(), true);
+ }
+
+ private static FilterInfo createFilterInfo(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
+ {
+ final BundleContext bCtx = mock(BundleContext.class);
+ when(bCtx.createFilter(Matchers.anyString())).thenReturn(null);
+ final Bundle bundle = mock(Bundle.class);
+ when(bundle.getBundleContext()).thenReturn(bCtx);
+
+ final ServiceReference<Filter> ref = mock(ServiceReference.class);
+ when(ref.getBundle()).thenReturn(bundle);
+ when(ref.getProperty(Constants.SERVICE_ID)).thenReturn(id);
+ when(ref.getProperty(Constants.SERVICE_RANKING)).thenReturn(ranking);
+ when(ref.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN)).thenReturn(paths);
+ when(ref.getPropertyKeys()).thenReturn(new String[0]);
+ final FilterInfo si = new FilterInfo(ref);
+
+ return si;
+ }
+
+ private static FilterHandler createFilterHandler(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
+ {
+ final FilterInfo si = createFilterInfo(id, ranking, paths);
+ final ExtServletContext ctx = mock(ExtServletContext.class);
+ final Filter filter = mock(Filter.class);
+
+ return new HttpServiceFilterHandler(ctx, si, filter);
+ }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/registry/FilterRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/FilterRegistryTest.java
index 702e007..4c23a94 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/registry/FilterRegistryTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/FilterRegistryTest.java
@@ -21,18 +21,17 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.FilterConfig;
+import java.util.EventListener;
+
+import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import org.apache.felix.http.base.internal.context.ExtServletContext;
-import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.HttpServiceFilterHandler;
-import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.handler.ListenerHandler;
+import org.apache.felix.http.base.internal.handler.WhiteboardListenerHandler;
+import org.apache.felix.http.base.internal.runtime.ListenerInfo;
import org.apache.felix.http.base.internal.runtime.dto.FailedDTOHolder;
import org.junit.Test;
import org.mockito.Matchers;
@@ -40,113 +39,89 @@
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.runtime.dto.ServletContextDTO;
-import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
public class FilterRegistryTest {
- private final FilterRegistry reg = new FilterRegistry();
-
private void assertEmpty(final ServletContextDTO dto, final FailedDTOHolder holder)
{
- assertNull(dto.filterDTOs);
- assertTrue(holder.failedFilterDTOs.isEmpty());
+ assertNull(dto.listenerDTOs);
+ assertTrue(holder.failedListenerDTOs.isEmpty());
}
private void clear(final ServletContextDTO dto, final FailedDTOHolder holder)
{
- dto.filterDTOs = null;
- holder.failedFilterDTOs.clear();
+ dto.listenerDTOs = null;
+ holder.failedListenerDTOs.clear();
}
- @Test public void testSingleFilter() throws InvalidSyntaxException, ServletException
+ @Test public void testSingleListener() throws InvalidSyntaxException, ServletException
{
+ final EventListenerRegistry reg = new EventListenerRegistry();
final FailedDTOHolder holder = new FailedDTOHolder();
final ServletContextDTO dto = new ServletContextDTO();
// check DTO
- reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
+ reg.getRuntimeInfo(dto, holder.failedListenerDTOs);
assertEmpty(dto, holder);
- // register filter
- final FilterHandler h1 = createFilterHandler(1L, 0, "/foo");
- reg.addFilter(h1);
-
- verify(h1.getFilter()).init(Matchers.any(FilterConfig.class));
+ // register listener
+ final ListenerHandler h1 = createListenerHandler(1L, 0, ServletContextListener.class);
+ reg.addListeners(h1);
// one entry in DTO
clear(dto, holder);
- reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
- assertTrue(holder.failedFilterDTOs.isEmpty());
- assertNotNull(dto.filterDTOs);
- assertEquals(1, dto.filterDTOs.length);
- assertEquals(1, dto.filterDTOs[0].patterns.length);
- assertEquals("/foo", dto.filterDTOs[0].patterns[0]);
+ reg.getRuntimeInfo(dto, holder.failedListenerDTOs);
+ assertTrue(holder.failedListenerDTOs.isEmpty());
+ assertNotNull(dto.listenerDTOs);
+ assertEquals(1, dto.listenerDTOs.length);
+ assertEquals(1, dto.listenerDTOs[0].types.length);
+ assertEquals(ServletContextListener.class.getName(), dto.listenerDTOs[0].types[0]);
- // remove filter
- final Filter f = h1.getFilter();
- reg.removeFilter(h1.getFilterInfo(), true);
- verify(f).destroy();
+ // remove listener
+ reg.removeListeners(h1.getListenerInfo());
// empty again
clear(dto, holder);
- reg.getRuntimeInfo(dto, holder.failedFilterDTOs);
+ reg.getRuntimeInfo(dto, holder.failedListenerDTOs);
assertEmpty(dto, holder);
}
- @Test public void testFilterOrdering() throws InvalidSyntaxException
+ private static ListenerInfo createListenerInfo(final long id, final int ranking, final Class<? extends EventListener> type) throws InvalidSyntaxException
{
- final FilterHandler h1 = createFilterHandler(1L, 20, "/foo");
- reg.addFilter(h1);
- final FilterHandler h2 = createFilterHandler(2L, 10, "/foo");
- reg.addFilter(h2);
- final FilterHandler h3 = createFilterHandler(3L, 30, "/foo");
- reg.addFilter(h3);
- final FilterHandler h4 = createFilterHandler(4L, 0, "/other");
- reg.addFilter(h4);
- final FilterHandler h5 = createFilterHandler(5L, 90, "/foo");
- reg.addFilter(h5);
+ final String[] typeNames = new String[1];
+ int index = 0;
+ typeNames[index++] = type.getName();
- final FilterHandler[] handlers = reg.getFilterHandlers(null, DispatcherType.REQUEST, "/foo");
- assertEquals(4, handlers.length);
- assertEquals(h5.getFilterInfo(), handlers[0].getFilterInfo());
- assertEquals(h3.getFilterInfo(), handlers[1].getFilterInfo());
- assertEquals(h1.getFilterInfo(), handlers[2].getFilterInfo());
- assertEquals(h2.getFilterInfo(), handlers[3].getFilterInfo());
-
- // cleanup
- reg.removeFilter(h1.getFilterInfo(), true);
- reg.removeFilter(h2.getFilterInfo(), true);
- reg.removeFilter(h3.getFilterInfo(), true);
- reg.removeFilter(h4.getFilterInfo(), true);
- reg.removeFilter(h5.getFilterInfo(), true);
- }
-
- private static FilterInfo createFilterInfo(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
- {
final BundleContext bCtx = mock(BundleContext.class);
when(bCtx.createFilter(Matchers.anyString())).thenReturn(null);
final Bundle bundle = mock(Bundle.class);
when(bundle.getBundleContext()).thenReturn(bCtx);
- final ServiceReference<Filter> ref = mock(ServiceReference.class);
+ final ServiceReference<EventListener> ref = mock(ServiceReference.class);
when(ref.getBundle()).thenReturn(bundle);
when(ref.getProperty(Constants.SERVICE_ID)).thenReturn(id);
when(ref.getProperty(Constants.SERVICE_RANKING)).thenReturn(ranking);
- when(ref.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN)).thenReturn(paths);
+ when(ref.getProperty(Constants.OBJECTCLASS)).thenReturn(typeNames);
when(ref.getPropertyKeys()).thenReturn(new String[0]);
- final FilterInfo si = new FilterInfo(ref);
- return si;
+ final EventListener listener = mock(type);
+ final ServiceObjects<EventListener> so = mock(ServiceObjects.class);
+ when(bCtx.getServiceObjects(ref)).thenReturn(so);
+ when(so.getService()).thenReturn(listener);
+
+ final ListenerInfo info = new ListenerInfo(ref);
+
+ return info;
}
- private static FilterHandler createFilterHandler(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
+ private static ListenerHandler createListenerHandler(final long id, final int ranking, final Class<? extends EventListener> type) throws InvalidSyntaxException
{
- final FilterInfo si = createFilterInfo(id, ranking, paths);
+ final ListenerInfo info = createListenerInfo(id, ranking, type);
final ExtServletContext ctx = mock(ExtServletContext.class);
- final Filter filter = mock(Filter.class);
- return new HttpServiceFilterHandler(ctx, si, filter);
+ return new WhiteboardListenerHandler(1L, ctx, info, info.getServiceReference().getBundle().getBundleContext());
}
}