View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.event;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.beans.PropertyChangeEvent;
26  import java.beans.PropertyChangeListener;
27  import java.beans.VetoableChangeListener;
28  import java.lang.reflect.Constructor;
29  import java.lang.reflect.InvocationHandler;
30  import java.lang.reflect.Method;
31  import java.lang.reflect.Modifier;
32  import java.lang.reflect.Proxy;
33  import java.util.Date;
34  import java.util.Map;
35  import java.util.TreeMap;
36  
37  import javax.naming.event.ObjectChangeListener;
38  
39  import org.apache.commons.lang3.AbstractLangTest;
40  import org.junit.jupiter.api.Test;
41  
42  /**
43   * @since 3.0
44   */
45  public class EventUtilsTest extends AbstractLangTest {
46      public static class EventCounter {
47          private int count;
48  
49          public void eventOccurred() {
50              count++;
51          }
52  
53          public int getCount() {
54              return count;
55          }
56      }
57  
58      public static class EventCounterWithEvent {
59          private int count;
60  
61          public void eventOccurred(final PropertyChangeEvent e) {
62              count++;
63          }
64  
65          public int getCount() {
66              return count;
67          }
68      }
69  
70      private static final class EventCountingInvocationHandler implements InvocationHandler {
71          private final Map<String, Integer> eventCounts = new TreeMap<>();
72  
73          public <L> L createListener(final Class<L> listenerType) {
74              return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
75                      new Class[]{listenerType},
76                      this));
77          }
78  
79          public int getEventCount(final String eventName) {
80              final Integer count = eventCounts.get(eventName);
81              return count == null ? 0 : count.intValue();
82          }
83  
84          @Override
85          public Object invoke(final Object proxy, final Method method, final Object[] args) {
86              final Integer count = eventCounts.get(method.getName());
87              if (count == null) {
88                  eventCounts.put(method.getName(), Integer.valueOf(1));
89              } else {
90                  eventCounts.put(method.getName(), Integer.valueOf(count.intValue() + 1));
91              }
92              return null;
93          }
94      }
95  
96      public static class ExceptionEventSource {
97          public void addPropertyChangeListener(final PropertyChangeListener listener) {
98              throw new RuntimeException();
99          }
100     }
101 
102     public interface MultipleEventListener {
103         void event1(PropertyChangeEvent e);
104 
105         void event2(PropertyChangeEvent e);
106     }
107 
108     public static class MultipleEventSource {
109         private final EventListenerSupport<MultipleEventListener> listeners = EventListenerSupport.create(MultipleEventListener.class);
110 
111         public void addMultipleEventListener(final MultipleEventListener listener) {
112             listeners.addListener(listener);
113         }
114     }
115 
116 
117     public static class PropertyChangeSource {
118         private final EventListenerSupport<PropertyChangeListener> listeners = EventListenerSupport.create(PropertyChangeListener.class);
119 
120         private String property;
121 
122         public void addPropertyChangeListener(final PropertyChangeListener listener) {
123             listeners.addListener(listener);
124         }
125 
126         protected void addVetoableChangeListener(final VetoableChangeListener listener) {
127             // Do nothing!
128         }
129 
130         public void removePropertyChangeListener(final PropertyChangeListener listener) {
131             listeners.removeListener(listener);
132         }
133 
134         public void setProperty(final String property) {
135             final String oldValue = this.property;
136             this.property = property;
137             listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property));
138         }
139     }
140 
141 
142     @Test
143     public void testAddEventListener() {
144         final PropertyChangeSource src = new PropertyChangeSource();
145         final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
146         final PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class);
147         assertEquals(0, handler.getEventCount("propertyChange"));
148         EventUtils.addEventListener(src, PropertyChangeListener.class, listener);
149         assertEquals(0, handler.getEventCount("propertyChange"));
150         src.setProperty("newValue");
151         assertEquals(1, handler.getEventCount("propertyChange"));
152     }
153 
154     @Test
155     public void testAddEventListenerThrowsException() {
156         final ExceptionEventSource src = new ExceptionEventSource();
157         assertThrows(RuntimeException.class, () ->
158             EventUtils.addEventListener(src, PropertyChangeListener.class, e -> {
159                 // Do nothing!
160             })
161         );
162     }
163 
164     @Test
165     public void testAddEventListenerWithNoAddMethod() {
166         final PropertyChangeSource src = new PropertyChangeSource();
167         final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
168         final ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class);
169         final IllegalArgumentException e =
170                 assertThrows(IllegalArgumentException.class, () -> EventUtils.addEventListener(src, ObjectChangeListener.class, listener));
171         assertEquals("Class " + src.getClass().getName() + " does not have a public add" + ObjectChangeListener.class.getSimpleName() + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".",
172                 e.getMessage());
173     }
174 
175     @Test
176     public void testAddEventListenerWithPrivateAddMethod() {
177         final PropertyChangeSource src = new PropertyChangeSource();
178         final EventCountingInvocationHandler handler = new EventCountingInvocationHandler();
179         final VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class);
180         final IllegalArgumentException e =
181                 assertThrows(IllegalArgumentException.class, () -> EventUtils.addEventListener(src, VetoableChangeListener.class, listener));
182         assertEquals("Class " + src.getClass().getName() + " does not have a public add" + VetoableChangeListener.class.getSimpleName() + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".",
183                 e.getMessage());
184     }
185 
186 
187     @Test
188     public void testBindEventsToMethod() {
189         final PropertyChangeSource src = new PropertyChangeSource();
190         final EventCounter counter = new EventCounter();
191         EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class);
192         assertEquals(0, counter.getCount());
193         src.setProperty("newValue");
194         assertEquals(1, counter.getCount());
195     }
196 
197     @Test
198     public void testBindEventsToMethodWithEvent() {
199         final PropertyChangeSource src = new PropertyChangeSource();
200         final EventCounterWithEvent counter = new EventCounterWithEvent();
201         EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class);
202         assertEquals(0, counter.getCount());
203         src.setProperty("newValue");
204         assertEquals(1, counter.getCount());
205     }
206 
207     @Test
208     public void testBindFilteredEventsToMethod() {
209         final MultipleEventSource src = new MultipleEventSource();
210         final EventCounter counter = new EventCounter();
211         EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1");
212         assertEquals(0, counter.getCount());
213         src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1)));
214         assertEquals(1, counter.getCount());
215         src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2)));
216         assertEquals(1, counter.getCount());
217     }
218 
219     @Test
220     public void testConstructor() {
221         assertNotNull(new EventUtils());
222         final Constructor<?>[] cons = EventUtils.class.getDeclaredConstructors();
223         assertEquals(1, cons.length);
224         assertTrue(Modifier.isPublic(cons[0].getModifiers()));
225         assertTrue(Modifier.isPublic(EventUtils.class.getModifiers()));
226         assertFalse(Modifier.isFinal(EventUtils.class.getModifiers()));
227     }
228 }