avoid registering several times the same listener on the same client
diff --git a/jspwiki-event/src/main/java/org/apache/wiki/event/WikiEventManager.java b/jspwiki-event/src/main/java/org/apache/wiki/event/WikiEventManager.java
index dc069e9..95728fd 100644
--- a/jspwiki-event/src/main/java/org/apache/wiki/event/WikiEventManager.java
+++ b/jspwiki-event/src/main/java/org/apache/wiki/event/WikiEventManager.java
@@ -29,6 +29,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
@@ -230,16 +231,17 @@
}
/**
- * Un-registers a WikiEventListener from any WikiEventDelegate client managed by this WikiEventManager. Since this is a one-to-one
- * relation, the first match will be returned upon removal; a true return value indicates the WikiEventListener was found and removed.
+ * Un-registers a WikiEventListener from any WikiEventDelegate client managed by this WikiEventManager. A true return value indicates
+ * the WikiEventListener was found and removed.
*
* @param listener the event listener
* @return true if the listener was found and removed.
*/
public static boolean removeWikiEventListener( final WikiEventListener listener ) {
+ boolean removed = false;
// get the Map.entry object for the entire Map, then check match on entry (listener)
final WikiEventManager mgr = getInstance();
- final Map< Object, WikiEventDelegate > sources = mgr.getDelegates();
+ final Map< Object, WikiEventDelegate > sources = Collections.synchronizedMap( mgr.getDelegates() );
synchronized( sources ) {
// get an iterator over the Map.Enty objects in the map
for( final Map.Entry< Object, WikiEventDelegate > entry : sources.entrySet() ) {
@@ -248,11 +250,11 @@
// now see if we can remove the listener from the delegate (delegate may be null because this is a weak reference)
if( delegate != null && delegate.removeWikiEventListener( listener ) ) {
- return true; // was removed
+ removed = true; // was removed
}
}
}
- return false;
+ return removed;
}
/**
@@ -387,13 +389,19 @@
/**
* Adds <tt>listener</tt> as a listener for events fired by the WikiEventDelegate.
*
- * @param listener the WikiEventListener to be added
+ * @param listener the WikiEventListener to be added
* @return true if the listener was added (i.e., it was not already in the list and was added)
*/
public boolean addWikiEventListener( final WikiEventListener listener ) {
synchronized( m_listenerList ) {
- return m_listenerList.add( new WeakReference<>(listener) );
+ final boolean listenerAlreadyContained = m_listenerList.stream()
+ .map( WeakReference::get )
+ .anyMatch( ref -> Objects.equals( ref, listener ) );
+ if( !listenerAlreadyContained ) {
+ return m_listenerList.add( new WeakReference<>( listener ) );
+ }
}
+ return false;
}
/**
diff --git a/jspwiki-event/src/test/java/org/apache/wiki/event/TestWikiEventListener.java b/jspwiki-event/src/test/java/org/apache/wiki/event/TestWikiEventListener.java
new file mode 100644
index 0000000..8372e0a
--- /dev/null
+++ b/jspwiki-event/src/test/java/org/apache/wiki/event/TestWikiEventListener.java
@@ -0,0 +1,35 @@
+/*
+ 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.wiki.event;
+
+public class TestWikiEventListener implements WikiEventListener {
+
+ int invoked = 0;
+
+ /** {@inheritDoc} */
+ @Override
+ public void actionPerformed( final WikiEvent event ) {
+ invoked++;
+ }
+
+ public int getInvoked() {
+ return invoked;
+ }
+
+}
diff --git a/jspwiki-event/src/test/java/org/apache/wiki/event/WikiEventManagerTest.java b/jspwiki-event/src/test/java/org/apache/wiki/event/WikiEventManagerTest.java
new file mode 100644
index 0000000..8902201
--- /dev/null
+++ b/jspwiki-event/src/test/java/org/apache/wiki/event/WikiEventManagerTest.java
@@ -0,0 +1,71 @@
+/*
+ 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.wiki.event;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+
+public class WikiEventManagerTest {
+
+ @Test
+ public void shouldCheckRegisterUnregister() {
+ final String client1 = "test1";
+ final String client2 = "test2";
+ final TestWikiEventListener listener = new TestWikiEventListener();
+ WikiEventManager.addWikiEventListener( client1, listener );
+ Assertions.assertEquals( 1, WikiEventManager.getWikiEventListeners( client1 ).size() );
+ Assertions.assertTrue( WikiEventManager.isListening( client1 ) );
+
+ WikiEventManager.removeWikiEventListener( client1, listener );
+ Assertions.assertEquals( 0, WikiEventManager.getWikiEventListeners( client1 ).size() );
+ Assertions.assertFalse( WikiEventManager.isListening( client1 ) );
+
+ WikiEventManager.addWikiEventListener( client1, listener );
+ WikiEventManager.addWikiEventListener( client2, listener );
+ WikiEventManager.removeWikiEventListener( listener );
+ Assertions.assertEquals( 0, WikiEventManager.getWikiEventListeners( client1 ).size() );
+ Assertions.assertEquals( 0, WikiEventManager.getWikiEventListeners( client2 ).size() );
+ }
+
+ @Test
+ public void shouldCheckAddingSameWikiEventListenerSeveralTimesOnlyGetsRegisteredOnce() {
+ final String client = "test3";
+ final TestWikiEventListener listener = new TestWikiEventListener();
+ WikiEventManager.addWikiEventListener( client, listener );
+ WikiEventManager.addWikiEventListener( client, listener );
+
+ Assertions.assertEquals( 1, WikiEventManager.getWikiEventListeners( client ).size() );
+
+ WikiEventManager.removeWikiEventListener( client, listener );
+ Assertions.assertEquals( 0, WikiEventManager.getWikiEventListeners( client ).size() );
+ }
+
+ @Test
+ public void shouldCheckEventsFiring() {
+ final String client = "test4";
+ final TestWikiEventListener listener = new TestWikiEventListener();
+ WikiEventManager.addWikiEventListener( client, listener );
+ WikiEventManager.fireEvent( client, new WikiPageEvent( "object which fires the event", WikiPageEvent.PAGE_REQUESTED, "page" ) );
+
+ Assertions.assertEquals( 1, listener.getInvoked() );
+ WikiEventManager.removeWikiEventListener( listener ); // dispose listener; if not done, listener would still be attached to test4 on other tests
+ }
+
+}