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 @Test testConstructor()47 public void testConstructor() { 48 assertNotNull(new EventUtils()); 49 final Constructor<?>[] cons = EventUtils.class.getDeclaredConstructors(); 50 assertEquals(1, cons.length); 51 assertTrue(Modifier.isPublic(cons[0].getModifiers())); 52 assertTrue(Modifier.isPublic(EventUtils.class.getModifiers())); 53 assertFalse(Modifier.isFinal(EventUtils.class.getModifiers())); 54 } 55 56 @Test testAddEventListener()57 public void testAddEventListener() { 58 final PropertyChangeSource src = new PropertyChangeSource(); 59 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler(); 60 final PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class); 61 assertEquals(0, handler.getEventCount("propertyChange")); 62 EventUtils.addEventListener(src, PropertyChangeListener.class, listener); 63 assertEquals(0, handler.getEventCount("propertyChange")); 64 src.setProperty("newValue"); 65 assertEquals(1, handler.getEventCount("propertyChange")); 66 } 67 68 @Test testAddEventListenerWithNoAddMethod()69 public void testAddEventListenerWithNoAddMethod() { 70 final PropertyChangeSource src = new PropertyChangeSource(); 71 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler(); 72 final ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class); 73 final IllegalArgumentException e = 74 assertThrows(IllegalArgumentException.class, () -> EventUtils.addEventListener(src, ObjectChangeListener.class, listener)); 75 assertEquals("Class " + src.getClass().getName() + " does not have a public add" + ObjectChangeListener.class.getSimpleName() + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".", 76 e.getMessage()); 77 } 78 79 @Test testAddEventListenerThrowsException()80 public void testAddEventListenerThrowsException() { 81 final ExceptionEventSource src = new ExceptionEventSource(); 82 assertThrows(RuntimeException.class, () -> 83 EventUtils.addEventListener(src, PropertyChangeListener.class, e -> { 84 // Do nothing! 85 }) 86 ); 87 } 88 89 @Test testAddEventListenerWithPrivateAddMethod()90 public void testAddEventListenerWithPrivateAddMethod() { 91 final PropertyChangeSource src = new PropertyChangeSource(); 92 final EventCountingInvocationHandler handler = new EventCountingInvocationHandler(); 93 final VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class); 94 final IllegalArgumentException e = 95 assertThrows(IllegalArgumentException.class, () -> EventUtils.addEventListener(src, VetoableChangeListener.class, listener)); 96 assertEquals("Class " + src.getClass().getName() + " does not have a public add" + VetoableChangeListener.class.getSimpleName() + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".", 97 e.getMessage()); 98 } 99 100 @Test testBindEventsToMethod()101 public void testBindEventsToMethod() { 102 final PropertyChangeSource src = new PropertyChangeSource(); 103 final EventCounter counter = new EventCounter(); 104 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); 105 assertEquals(0, counter.getCount()); 106 src.setProperty("newValue"); 107 assertEquals(1, counter.getCount()); 108 } 109 110 111 @Test testBindEventsToMethodWithEvent()112 public void testBindEventsToMethodWithEvent() { 113 final PropertyChangeSource src = new PropertyChangeSource(); 114 final EventCounterWithEvent counter = new EventCounterWithEvent(); 115 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); 116 assertEquals(0, counter.getCount()); 117 src.setProperty("newValue"); 118 assertEquals(1, counter.getCount()); 119 } 120 121 122 @Test testBindFilteredEventsToMethod()123 public void testBindFilteredEventsToMethod() { 124 final MultipleEventSource src = new MultipleEventSource(); 125 final EventCounter counter = new EventCounter(); 126 EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1"); 127 assertEquals(0, counter.getCount()); 128 src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1))); 129 assertEquals(1, counter.getCount()); 130 src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2))); 131 assertEquals(1, counter.getCount()); 132 } 133 134 public interface MultipleEventListener { event1(PropertyChangeEvent e)135 void event1(PropertyChangeEvent e); 136 event2(PropertyChangeEvent e)137 void event2(PropertyChangeEvent e); 138 } 139 140 public static class EventCounter { 141 private int count; 142 eventOccurred()143 public void eventOccurred() { 144 count++; 145 } 146 getCount()147 public int getCount() { 148 return count; 149 } 150 } 151 152 public static class EventCounterWithEvent { 153 private int count; 154 eventOccurred(final PropertyChangeEvent e)155 public void eventOccurred(final PropertyChangeEvent e) { 156 count++; 157 } 158 getCount()159 public int getCount() { 160 return count; 161 } 162 } 163 164 165 private static class EventCountingInvocationHandler implements InvocationHandler { 166 private final Map<String, Integer> eventCounts = new TreeMap<>(); 167 createListener(final Class<L> listenerType)168 public <L> L createListener(final Class<L> listenerType) { 169 return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 170 new Class[]{listenerType}, 171 this)); 172 } 173 getEventCount(final String eventName)174 public int getEventCount(final String eventName) { 175 final Integer count = eventCounts.get(eventName); 176 return count == null ? 0 : count.intValue(); 177 } 178 179 @Override invoke(final Object proxy, final Method method, final Object[] args)180 public Object invoke(final Object proxy, final Method method, final Object[] args) { 181 final Integer count = eventCounts.get(method.getName()); 182 if (count == null) { 183 eventCounts.put(method.getName(), Integer.valueOf(1)); 184 } else { 185 eventCounts.put(method.getName(), Integer.valueOf(count.intValue() + 1)); 186 } 187 return null; 188 } 189 } 190 191 public static class MultipleEventSource { 192 private final EventListenerSupport<MultipleEventListener> listeners = EventListenerSupport.create(MultipleEventListener.class); 193 addMultipleEventListener(final MultipleEventListener listener)194 public void addMultipleEventListener(final MultipleEventListener listener) { 195 listeners.addListener(listener); 196 } 197 } 198 199 public static class ExceptionEventSource { addPropertyChangeListener(final PropertyChangeListener listener)200 public void addPropertyChangeListener(final PropertyChangeListener listener) { 201 throw new RuntimeException(); 202 } 203 } 204 205 public static class PropertyChangeSource { 206 private final EventListenerSupport<PropertyChangeListener> listeners = EventListenerSupport.create(PropertyChangeListener.class); 207 208 private String property; 209 setProperty(final String property)210 public void setProperty(final String property) { 211 final String oldValue = this.property; 212 this.property = property; 213 listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property)); 214 } 215 addVetoableChangeListener(final VetoableChangeListener listener)216 protected void addVetoableChangeListener(final VetoableChangeListener listener) { 217 // Do nothing! 218 } 219 addPropertyChangeListener(final PropertyChangeListener listener)220 public void addPropertyChangeListener(final PropertyChangeListener listener) { 221 listeners.addListener(listener); 222 } 223 removePropertyChangeListener(final PropertyChangeListener listener)224 public void removePropertyChangeListener(final PropertyChangeListener listener) { 225 listeners.removeListener(listener); 226 } 227 } 228 } 229