/* * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 4682386 * @summary Tests for PropertyChangeSupport refactoring * @author Mark Davidson */ import java.beans.Beans; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JProgressBar; import javax.swing.JTextArea; import javax.swing.JTextPane; import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.JTabbedPane; import javax.swing.JTree; import javax.swing.JTable; /** * This class tests the multi-threaded access to PropertyChangeSupport and * will also use reflection to test propertyChanges on Swing components. * <p/> * There is no new functionality from the implementation of this RFE. * Semantically, it should be equivalent. */ public class Test4682386 { private static final String FOO = "foo"; private static final String BAR = "bar"; private static final int NUM_LISTENERS = 100; private static final boolean DEBUG = true; private static final Class[] TYPES = { JApplet.class, JButton.class, JCheckBox.class, JComboBox.class, JLabel.class, JList.class, JMenuItem.class, JProgressBar.class, JTextArea.class, JTextPane.class, JTextField.class, JToolBar.class, JTabbedPane.class, JTree.class, JTable.class, }; public static void main(String[] args) { testSwingProperties(); // tests the multi-threaded access TestBean bean = new TestBean(); Thread add = new Thread(new AddThread(bean)); Thread remove = new Thread(new RemoveThread(bean)); Thread prop = new Thread(new PropertyThread(bean)); add.start(); prop.start(); remove.start(); } /** * Should be exectuted with $JAVA_HOME/lib/dt.jar in the classpath * so that there will be a lot more bound properties. * <p/> * This test isn't really appropriate for automated testing. */ private static void testSwingProperties() { long start = System.currentTimeMillis(); for (Class type : TYPES) { try { Object bean = Beans.instantiate(type.getClassLoader(), type.getName()); JComponent comp = (JComponent) bean; for (int k = 0; k < NUM_LISTENERS; k++) { comp.addPropertyChangeListener(new PropertyListener()); } for (PropertyDescriptor pd : getPropertyDescriptors(type)) { if (pd.isBound()) { if (DEBUG) { System.out.println("Bound property found: " + pd.getName()); } Method read = pd.getReadMethod(); Method write = pd.getWriteMethod(); try { write.invoke( bean, getValue( pd.getPropertyType(), read.invoke(bean))); } catch (Exception ex) { // do nothing - just move on. if (DEBUG) { System.out.println("Reflective method invocation Exception for " + type + " : " + ex.getMessage()); } } } } } catch (Exception ex) { // do nothing - just move on. if (DEBUG) { System.out.println("Exception for " + type.getName() + " : " + ex.getMessage()); } } } System.out.println("Exec time (ms): " + (System.currentTimeMillis() - start)); } /** * Gets a fake value from a type and old value; */ public static Object getValue(Class type, Object value) { if (String.class.equals(type)) { return "test string"; } if (value instanceof Integer) { Integer i = (Integer) value; return Integer.valueOf(i + 1); } if (value instanceof Boolean) { Boolean b = (Boolean) value; return Boolean.valueOf(!b); } return null; } public static PropertyDescriptor[] getPropertyDescriptors(Class type) { try { return Introspector.getBeanInfo(type).getPropertyDescriptors(); } catch (IntrospectionException exception) { throw new Error("unexpected exception", exception); } } private static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException exception) { } } private static class AddThread implements Runnable { private final TestBean bean; AddThread(TestBean bean) { this.bean = bean; } public void run() { for (int i = 0; i < NUM_LISTENERS; i++) { for (int j = 0; j < 10; j++) { this.bean.addPropertyChangeListener(new PropertyListener()); } if (DEBUG) { System.out.println("10 listeners added"); } sleep(25L); } } } private static class RemoveThread implements Runnable { private final TestBean bean; RemoveThread(TestBean bean) { this.bean = bean; } public void run() { for (int k = 0; k < NUM_LISTENERS; k++) { sleep(100L); PropertyChangeListener[] listeners = this.bean.getPropertyChangeListners(); for (int i = listeners.length - 1; i >= 0; i--) { this.bean.removePropertyChangeListener(listeners[i]); } if (DEBUG) { System.out.println(listeners.length + " listeners removed"); } } } } private static class PropertyThread implements Runnable { private final TestBean bean; PropertyThread(TestBean bean) { this.bean = bean; } public void run() { for (int i = 0; i < NUM_LISTENERS; i++) { boolean flag = this.bean.isFoo(); this.bean.setFoo(!flag); this.bean.setBar(Boolean.toString(flag)); if (DEBUG) { System.out.println("Executed property changes"); } sleep(40L); } } } /** * Handler for the property change events. */ private static class PropertyListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { // blank since this should execute as fast as possible. } } /** * A simple bean to test multi-threaded acccess to the listeners. */ public static class TestBean { private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private boolean foo; private String bar; public void addPropertyChangeListener(PropertyChangeListener listener) { this.pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { this.pcs.removePropertyChangeListener(listener); } public PropertyChangeListener[] getPropertyChangeListners() { return this.pcs.getPropertyChangeListeners(); } public boolean isFoo() { return this.foo; } public void setFoo(boolean foo) { boolean old = this.foo; this.foo = foo; this.pcs.firePropertyChange(FOO, old, foo); } public String getBar() { return this.bar; } public void setBar(String bar) { String old = this.bar; this.bar = bar; this.pcs.firePropertyChange(BAR, old, bar); } } }