8004874: Reduce dependency on java.beans to only add/removePropertyChangeListener
Reviewed-by: ksrini, mchung, dholmes
--- 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);
+ }
+ }
+ }
}