OpenJDK / amber / amber
changeset 14774:4e298ffae1c7
8004874: Reduce dependency on java.beans to only add/removePropertyChangeListener
Reviewed-by: ksrini, mchung, dholmes
author | alanb |
---|---|
date | Wed, 12 Dec 2012 13:03:05 +0000 |
parents | aac1f6801eec |
children | d6a7921d26f7 |
files | jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java jdk/src/share/classes/java/util/logging/LogManager.java |
diffstat | 2 files changed, 231 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java Wed Dec 12 11:35:18 2012 +0000 +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java Wed Dec 12 13:03:05 2012 +0000 @@ -25,8 +25,6 @@ package com.sun.java.util.jar.pack; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeEvent; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; @@ -42,40 +40,39 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.jar.Pack200; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** * Control block for publishing Pack200 options to the other classes. */ final class PropMap implements SortedMap<String, String> { private final TreeMap<String, String> theMap = new TreeMap<>();; - private final List<PropertyChangeListener> listenerList = new ArrayList<>(1); - void addListener(PropertyChangeListener listener) { + // type is erased, elements are of type java.beans.PropertyChangeListener + private final List<Object> listenerList = new ArrayList<>(1); + + void addListener(Object listener) { + assert Beans.isPropertyChangeListener(listener); listenerList.add(listener); } - void removeListener(PropertyChangeListener listener) { + void removeListener(Object listener) { + assert Beans.isPropertyChangeListener(listener); listenerList.remove(listener); } - void addListeners(ArrayList<PropertyChangeListener> listeners) { - listenerList.addAll(listeners); - } - - void removeListeners(ArrayList<PropertyChangeListener> listeners) { - listenerList.removeAll(listeners); - } - // Override: public String put(String key, String value) { String oldValue = theMap.put(key, value); if (value != oldValue && !listenerList.isEmpty()) { + assert Beans.isBeansPresent(); // Post the property change event. - PropertyChangeEvent event = - new PropertyChangeEvent(this, key, - oldValue, value); - for (PropertyChangeListener listener : listenerList) { - listener.propertyChange(event); + Object event = Beans.newPropertyChangeEvent(this, key, oldValue, value); + for (Object listener : listenerList) { + Beans.invokePropertyChange(listener, event); } } return oldValue; @@ -339,4 +336,113 @@ public String lastKey() { return theMap.lastKey(); } + + /** + * A class that provides access to the java.beans.PropertyChangeListener + * and java.beans.PropertyChangeEvent without creating a static dependency + * on java.beans. This class can be removed once the addPropertyChangeListener + * and removePropertyChangeListener methods are removed from Packer and + * Unpacker. + */ + private static class Beans { + private static final Class<?> propertyChangeListenerClass = + getClass("java.beans.PropertyChangeListener"); + + private static final Class<?> propertyChangeEventClass = + getClass("java.beans.PropertyChangeEvent"); + + private static final Method propertyChangeMethod = + getMethod(propertyChangeListenerClass, + "propertyChange", + propertyChangeEventClass); + + private static final Constructor<?> propertyEventCtor = + getConstructor(propertyChangeEventClass, + Object.class, + String.class, + Object.class, + Object.class); + + private static Class<?> getClass(String name) { + try { + return Class.forName(name, true, Beans.class.getClassLoader()); + } catch (ClassNotFoundException e) { + return null; + } + } + private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { + try { + return (c == null) ? null : c.getDeclaredConstructor(types); + } catch (NoSuchMethodException x) { + throw new AssertionError(x); + } + } + + private static Method getMethod(Class<?> c, String name, Class<?>... types) { + try { + return (c == null) ? null : c.getMethod(name, types); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + /** + * Returns {@code true} if java.beans is present. + */ + static boolean isBeansPresent() { + return propertyChangeListenerClass != null && + propertyChangeEventClass != null; + } + + /** + * Returns {@code true} if the given object is a PropertyChangeListener + */ + static boolean isPropertyChangeListener(Object obj) { + if (propertyChangeListenerClass == null) { + return false; + } else { + return propertyChangeListenerClass.isInstance(obj); + } + } + + /** + * Returns a new PropertyChangeEvent with the given source, property + * name, old and new values. + */ + static Object newPropertyChangeEvent(Object source, String prop, + Object oldValue, Object newValue) + { + try { + return propertyEventCtor.newInstance(source, prop, oldValue, newValue); + } catch (InstantiationException | IllegalAccessException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + Throwable cause = x.getCause(); + if (cause instanceof Error) + throw (Error)cause; + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; + throw new AssertionError(x); + } + } + + /** + * Invokes the given PropertyChangeListener's propertyChange method + * with the given event. + */ + static void invokePropertyChange(Object listener, Object ev) { + try { + propertyChangeMethod.invoke(listener, ev); + } catch (IllegalAccessException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + Throwable cause = x.getCause(); + if (cause instanceof Error) + throw (Error)cause; + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; + throw new AssertionError(x); + } + } + } }
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Wed Dec 12 11:35:18 2012 +0000 +++ b/jdk/src/share/classes/java/util/logging/LogManager.java Wed Dec 12 13:03:05 2012 +0000 @@ -31,10 +31,10 @@ import java.security.*; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeEvent; -import java.net.URL; -import sun.security.action.GetPropertyAction; /** * There is a single global LogManager object that is used to @@ -150,7 +150,7 @@ // The map of the registered listeners. The map value is the registration // count to allow for cases where the same listener is registered many times. - private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>(); + private final Map<Object,Integer> listenerMap = new HashMap<>(); // Table of named Loggers that maps names to Loggers. private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); @@ -971,22 +971,24 @@ // Notify any interested parties that our properties have changed. // We first take a copy of the listener map so that we aren't holding any // locks when calling the listeners. - Map<PropertyChangeListener,Integer> listeners = null; + Map<Object,Integer> listeners = null; synchronized (listenerMap) { if (!listenerMap.isEmpty()) listeners = new HashMap<>(listenerMap); } if (listeners != null) { - PropertyChangeEvent ev = new PropertyChangeEvent(LogManager.class, null, null, null); - for (Map.Entry<PropertyChangeListener,Integer> entry : listeners.entrySet()) { - PropertyChangeListener listener = entry.getKey(); + assert Beans.isBeansPresent(); + Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null); + for (Map.Entry<Object,Integer> entry : listeners.entrySet()) { + Object listener = entry.getKey(); int count = entry.getValue().intValue(); for (int i = 0; i < count; i++) { - listener.propertyChange(ev); + Beans.invokePropertyChange(listener, ev); } } } + // Note that we need to reinitialize global handles when // they are first referenced. synchronized (this) { @@ -1269,4 +1271,100 @@ return loggingMXBean; } + /** + * A class that provides access to the java.beans.PropertyChangeListener + * and java.beans.PropertyChangeEvent without creating a static dependency + * on java.beans. This class can be removed once the addPropertyChangeListener + * and removePropertyChangeListener methods are removed. + */ + private static class Beans { + private static final Class<?> propertyChangeListenerClass = + getClass("java.beans.PropertyChangeListener"); + + private static final Class<?> propertyChangeEventClass = + getClass("java.beans.PropertyChangeEvent"); + + private static final Method propertyChangeMethod = + getMethod(propertyChangeListenerClass, + "propertyChange", + propertyChangeEventClass); + + private static final Constructor<?> propertyEventCtor = + getConstructor(propertyChangeEventClass, + Object.class, + String.class, + Object.class, + Object.class); + + private static Class<?> getClass(String name) { + try { + return Class.forName(name, true, Beans.class.getClassLoader()); + } catch (ClassNotFoundException e) { + return null; + } + } + private static Constructor<?> getConstructor(Class<?> c, Class<?>... types) { + try { + return (c == null) ? null : c.getDeclaredConstructor(types); + } catch (NoSuchMethodException x) { + throw new AssertionError(x); + } + } + + private static Method getMethod(Class<?> c, String name, Class<?>... types) { + try { + return (c == null) ? null : c.getMethod(name, types); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + /** + * Returns {@code true} if java.beans is present. + */ + static boolean isBeansPresent() { + return propertyChangeListenerClass != null && + propertyChangeEventClass != null; + } + + /** + * Returns a new PropertyChangeEvent with the given source, property + * name, old and new values. + */ + static Object newPropertyChangeEvent(Object source, String prop, + Object oldValue, Object newValue) + { + try { + return propertyEventCtor.newInstance(source, prop, oldValue, newValue); + } catch (InstantiationException | IllegalAccessException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + Throwable cause = x.getCause(); + if (cause instanceof Error) + throw (Error)cause; + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; + throw new AssertionError(x); + } + } + + /** + * Invokes the given PropertyChangeListener's propertyChange method + * with the given event. + */ + static void invokePropertyChange(Object listener, Object ev) { + try { + propertyChangeMethod.invoke(listener, ev); + } catch (IllegalAccessException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + Throwable cause = x.getCause(); + if (cause instanceof Error) + throw (Error)cause; + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; + throw new AssertionError(x); + } + } + } }