From 2c3b2dd68ee917b08e28ba05315285076778ed2d Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Tue, 22 Sep 2015 14:05:37 +0400 Subject: [PATCH] 8133453: Deprecate AWTKeyStroke.registerSubclass(Class) method Reviewed-by: serb, azvegint --- .../share/classes/java/awt/AWTKeyStroke.java | 125 ++------------- .../share/classes/javax/swing/KeyStroke.java | 17 +- .../classes/sun/swing/SwingAccessor.java | 30 ++++ .../RegisterKeyStroke/TestAWTKeyStroke.java | 148 ++++++++++++++++++ .../event/KeyEvent/RegisterKeyStroke/policy | 2 + 5 files changed, 201 insertions(+), 121 deletions(-) create mode 100644 jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/TestAWTKeyStroke.java create mode 100644 jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/policy diff --git a/jdk/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java b/jdk/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java index 67eff6256fb..f49b16badd2 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java +++ b/jdk/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java @@ -32,12 +32,9 @@ import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import java.io.Serializable; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.Field; +import sun.swing.SwingAccessor; /** * An AWTKeyStroke represents a key action on the @@ -80,21 +77,6 @@ public class AWTKeyStroke implements Serializable { //A key withing the cache private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke(); - /* - * Reads keystroke class from AppContext and if null, puts there the - * AWTKeyStroke class. - * Must be called under locked AWTKeyStroke - */ - private static Class getAWTKeyStrokeClass() { - @SuppressWarnings("unchecked") - Class clazz = (Class)AppContext.getAppContext().get(AWTKeyStroke.class); - if (clazz == null) { - clazz = AWTKeyStroke.class; - AppContext.getAppContext().put(AWTKeyStroke.class, AWTKeyStroke.class); - } - return clazz; - } - private char keyChar = KeyEvent.CHAR_UNDEFINED; private int keyCode = KeyEvent.VK_UNDEFINED; private int modifiers; @@ -160,92 +142,15 @@ public class AWTKeyStroke implements Serializable { } /** - * Registers a new class which the factory methods in - * AWTKeyStroke will use when generating new - * instances of AWTKeyStrokes. After invoking this - * method, the factory methods will return instances of the specified - * Class. The specified Class must be either AWTKeyStroke - * or derived from AWTKeyStroke, and it must have a - * no-arg constructor. The constructor can be of any accessibility, - * including private. This operation - * flushes the current AWTKeyStroke cache. + * The method has no effect and is only left present to avoid introducing + * a binary incompatibility. * * @param subclass the new Class of which the factory methods should create * instances - * @throws IllegalArgumentException if subclass is null, - * or if subclass does not have a no-arg constructor - * @throws ClassCastException if subclass is not - * AWTKeyStroke, or a class derived from - * AWTKeyStroke + * @deprecated */ + @Deprecated protected static void registerSubclass(Class subclass) { - if (subclass == null) { - throw new IllegalArgumentException("subclass cannot be null"); - } - synchronized (AWTKeyStroke.class) { - @SuppressWarnings("unchecked") - Class keyStrokeClass = (Class)AppContext.getAppContext().get(AWTKeyStroke.class); - if (keyStrokeClass != null && keyStrokeClass.equals(subclass)){ - // Already registered - return; - } - } - if (!AWTKeyStroke.class.isAssignableFrom(subclass)) { - throw new ClassCastException("subclass is not derived from AWTKeyStroke"); - } - - Constructor ctor = getCtor(subclass); - - String couldNotInstantiate = "subclass could not be instantiated"; - - if (ctor == null) { - throw new IllegalArgumentException(couldNotInstantiate); - } - try { - AWTKeyStroke stroke = (AWTKeyStroke)ctor.newInstance((Object[]) null); - if (stroke == null) { - throw new IllegalArgumentException(couldNotInstantiate); - } - } catch (NoSuchMethodError e) { - throw new IllegalArgumentException(couldNotInstantiate); - } catch (ExceptionInInitializerError e) { - throw new IllegalArgumentException(couldNotInstantiate); - } catch (InstantiationException e) { - throw new IllegalArgumentException(couldNotInstantiate); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException(couldNotInstantiate); - } catch (InvocationTargetException e) { - throw new IllegalArgumentException(couldNotInstantiate); - } - - synchronized (AWTKeyStroke.class) { - AppContext.getAppContext().put(AWTKeyStroke.class, subclass); - AppContext.getAppContext().remove(APP_CONTEXT_CACHE_KEY); - AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY); - } - } - - /* returns no-arg Constructor for class with accessible flag. No security - threat as accessible flag is set only for this Constructor object, - not for Class constructor. - */ - private static Constructor getCtor(final Class clazz) - { - Constructor ctor = AccessController.doPrivileged(new PrivilegedAction>() { - public Constructor run() { - try { - Constructor ctor = clazz.getDeclaredConstructor((Class[]) null); - if (ctor != null) { - ctor.setAccessible(true); - } - return ctor; - } catch (SecurityException e) { - } catch (NoSuchMethodException e) { - } - return null; - } - }); - return ctor; } private static synchronized AWTKeyStroke getCachedStroke @@ -261,18 +166,10 @@ public class AWTKeyStroke implements Serializable { } if (cacheKey == null) { - try { - Class clazz = getAWTKeyStrokeClass(); - cacheKey = (AWTKeyStroke)getCtor(clazz).newInstance((Object[]) null); - AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); - } catch (InstantiationException e) { - assert(false); - } catch (IllegalAccessException e) { - assert(false); - } catch (InvocationTargetException e) { - assert(false); - } + cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); + AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); } + cacheKey.keyChar = keyChar; cacheKey.keyCode = keyCode; cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); @@ -806,11 +703,9 @@ public class AWTKeyStroke implements Serializable { */ protected Object readResolve() throws java.io.ObjectStreamException { synchronized (AWTKeyStroke.class) { - if (getClass().equals(getAWTKeyStrokeClass())) { - return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); - } + + return getCachedStroke(keyChar, keyCode, modifiers, onKeyRelease); } - return this; } private static int mapOldModifiers(int modifiers) { diff --git a/jdk/src/java.desktop/share/classes/javax/swing/KeyStroke.java b/jdk/src/java.desktop/share/classes/javax/swing/KeyStroke.java index 24969f76aec..ebe6a126e55 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/KeyStroke.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/KeyStroke.java @@ -26,6 +26,7 @@ package javax.swing; import java.awt.AWTKeyStroke; import java.awt.event.KeyEvent; +import sun.swing.SwingAccessor; /** * A KeyStroke represents a key action on the keyboard, or equivalent input @@ -70,6 +71,16 @@ public class KeyStroke extends AWTKeyStroke { */ private static final long serialVersionUID = -9060180771037902530L; + static { + SwingAccessor.setKeyStrokeAccessor(new SwingAccessor.KeyStrokeAccessor() { + + @Override + public KeyStroke create() { + return new KeyStroke(); + } + }); + } + private KeyStroke() { } private KeyStroke(char keyChar, int keyCode, int modifiers, @@ -87,7 +98,6 @@ public class KeyStroke extends AWTKeyStroke { */ public static KeyStroke getKeyStroke(char keyChar) { synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); return (KeyStroke)getAWTKeyStroke(keyChar); } } @@ -148,7 +158,6 @@ public class KeyStroke extends AWTKeyStroke { */ public static KeyStroke getKeyStroke(Character keyChar, int modifiers) { synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); return (KeyStroke)getAWTKeyStroke(keyChar, modifiers); } } @@ -199,7 +208,6 @@ public class KeyStroke extends AWTKeyStroke { public static KeyStroke getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease) { synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); return (KeyStroke)getAWTKeyStroke(keyCode, modifiers, onKeyRelease); } @@ -247,7 +255,6 @@ public class KeyStroke extends AWTKeyStroke { */ public static KeyStroke getKeyStroke(int keyCode, int modifiers) { synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); return (KeyStroke)getAWTKeyStroke(keyCode, modifiers); } } @@ -266,7 +273,6 @@ public class KeyStroke extends AWTKeyStroke { */ public static KeyStroke getKeyStrokeForEvent(KeyEvent anEvent) { synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); return (KeyStroke)getAWTKeyStrokeForEvent(anEvent); } } @@ -307,7 +313,6 @@ public class KeyStroke extends AWTKeyStroke { return null; } synchronized (AWTKeyStroke.class) { - registerSubclass(KeyStroke.class); try { return (KeyStroke)getAWTKeyStroke(s); } catch (IllegalArgumentException e) { diff --git a/jdk/src/java.desktop/share/classes/sun/swing/SwingAccessor.java b/jdk/src/java.desktop/share/classes/sun/swing/SwingAccessor.java index 37aa744a69b..da637f0625c 100644 --- a/jdk/src/java.desktop/share/classes/sun/swing/SwingAccessor.java +++ b/jdk/src/java.desktop/share/classes/sun/swing/SwingAccessor.java @@ -97,6 +97,14 @@ public final class SwingAccessor { int ownerX, int ownerY); } + /* + * An accessor for the KeyStroke class + */ + public interface KeyStrokeAccessor { + + KeyStroke create(); + } + /** * The javax.swing.text.JTextComponent class accessor object. */ @@ -185,4 +193,26 @@ public final class SwingAccessor { public static void setPopupFactoryAccessor(PopupFactoryAccessor popupFactoryAccessor) { SwingAccessor.popupFactoryAccessor = popupFactoryAccessor; } + + /** + * The KeyStroke class accessor object. + */ + private static KeyStrokeAccessor keyStrokeAccessor; + + /** + * Retrieve the accessor object for the KeyStroke class. + */ + public static KeyStrokeAccessor getKeyStrokeAccessor() { + if (keyStrokeAccessor == null) { + unsafe.ensureClassInitialized(KeyStroke.class); + } + return keyStrokeAccessor; + } + + /* + * Set the accessor object for the KeyStroke class. + */ + public static void setKeyStrokeAccessor(KeyStrokeAccessor accessor) { + SwingAccessor.keyStrokeAccessor = accessor; + } } diff --git a/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/TestAWTKeyStroke.java b/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/TestAWTKeyStroke.java new file mode 100644 index 00000000000..a6221b480c5 --- /dev/null +++ b/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/TestAWTKeyStroke.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, 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. + */ + +import java.awt.AWTKeyStroke; +import java.awt.event.InputEvent; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import javax.swing.KeyStroke; + +/* + * @test + * @bug 8133453 + * @summary Remove AWTKeyStroke.registerSubclass(Class) method + * @author Alexander Scherbatiy + * @run main/othervm TestAWTKeyStroke + * @run main/othervm/policy=policy -Djava.security.manager TestAWTKeyStroke + */ +public class TestAWTKeyStroke { + + public static void main(String[] args) throws Exception { + + int modifiers = InputEvent.CTRL_MASK | InputEvent.CTRL_DOWN_MASK; + checkAWTKeyStroke('A', modifiers, true); + checkKeyStroke('B', modifiers, false); + checkAWTKeyStroke('C', modifiers, false); + checkKeyStroke('D', modifiers, true); + checkSerializedKeyStrokes('E', modifiers, true); + } + + private static void checkAWTKeyStroke(int keyCode, int modifiers, + boolean onKeyRelease) throws Exception { + + AWTKeyStroke awtKeyStroke1 = AWTKeyStroke.getAWTKeyStroke( + keyCode, modifiers, onKeyRelease); + + checkAWTKeyStroke(awtKeyStroke1, keyCode, modifiers, onKeyRelease); + + AWTKeyStroke awtKeyStroke2 = AWTKeyStroke.getAWTKeyStroke( + keyCode, modifiers, onKeyRelease); + + if (awtKeyStroke1 != awtKeyStroke2) { + throw new RuntimeException("AWTKeyStroke is not cached!"); + } + + checkSerializedKeyStroke(awtKeyStroke1); + } + + private static void checkKeyStroke(int keyCode, int modifiers, + boolean onKeyRelease) throws Exception { + + KeyStroke keyStroke1 = KeyStroke.getKeyStroke( + keyCode, modifiers, onKeyRelease); + checkAWTKeyStroke(keyStroke1, keyCode, modifiers, onKeyRelease); + + KeyStroke keyStroke2 = KeyStroke.getKeyStroke( + keyCode, modifiers, onKeyRelease); + + if (keyStroke1 != keyStroke2) { + throw new RuntimeException("KeyStroke is not cached!"); + } + + checkSerializedKeyStroke(keyStroke1); + } + + private static void checkSerializedKeyStrokes(int keyCode, int modifiers, + boolean onKeyRelease) throws Exception { + + AWTKeyStroke awtKeyStroke = AWTKeyStroke.getAWTKeyStroke( + keyCode, modifiers, onKeyRelease); + + KeyStroke keyStroke = KeyStroke.getKeyStroke( + keyCode, modifiers, onKeyRelease); + + if (awtKeyStroke != getSerializedAWTKeyStroke(awtKeyStroke)) { + throw new RuntimeException("Serialized AWTKeyStroke is not cached!"); + } + + awtKeyStroke = AWTKeyStroke.getAWTKeyStroke( + keyCode, modifiers, !onKeyRelease); + + if (!keyStroke.equals(getSerializedAWTKeyStroke(keyStroke))) { + throw new RuntimeException("Serialized KeyStroke is not cached!"); + } + } + + private static void checkAWTKeyStroke(AWTKeyStroke awtKeyStroke, + int keyCode, int modifiers, boolean onKeyRelease) { + + if (awtKeyStroke.getKeyCode() != keyCode) { + throw new RuntimeException("Wrong key code!"); + } + + if (awtKeyStroke.getModifiers() != modifiers) { + throw new RuntimeException("Wrong modifiers!"); + } + + if (awtKeyStroke.isOnKeyRelease() != onKeyRelease) { + throw new RuntimeException("Wrong on key release!"); + } + } + + private static void checkSerializedKeyStroke(AWTKeyStroke keyStroke) + throws Exception { + if (keyStroke != getSerializedAWTKeyStroke(keyStroke)) { + throw new RuntimeException("New instance is returned during" + + " serialization!"); + } + } + + private static AWTKeyStroke getSerializedAWTKeyStroke(AWTKeyStroke keyStroke) + throws Exception { + + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = new ObjectOutputStream(bos)) { + out.writeObject(keyStroke); + byte[] bytes = bos.toByteArray(); + + try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInput in = new ObjectInputStream(bis)) { + return (AWTKeyStroke) in.readObject(); + } + } + } +} diff --git a/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/policy b/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/policy new file mode 100644 index 00000000000..e89913b9f24 --- /dev/null +++ b/jdk/test/java/awt/event/KeyEvent/RegisterKeyStroke/policy @@ -0,0 +1,2 @@ +grant { +};