-
Enhancement
-
Resolution: Won't Fix
-
P4
-
9
-
x86
-
other
A DESCRIPTION OF THE REQUEST :
Jigsaw will disallow access to internal packages.
We have written a complex validation and focus management implementation for our closed-source RIA and maintain it since JRE 1.4.
For this we use some internal API. We have a custom implementation of these interfaces:
sun.awt.RequestFocusController
sun.awt.AWTAccessor.ComponentAccessor
And use this getter and setter:
sun.awt.AWTAccessor#setComponentAccessor(AWTAccessor.ComponentAccessor)
sun.awt.AWTAccessor#getComponentAccessor()
sun.awt.AWTAccessor.ComponentAccessor#setRequestFocusController(RequestFocusController)
Please make this API public.
Basically we need to detect and intercept focus changes.
java.awt.Component allows to override this methods:
requestFocus()
requestFocus(boolean)
requestFocusInWindow()
requestFocusInWindow(boolean)
requestFocus() is only invoked when the component is focused by a mouse click.
But no requestFocus* method is invoked when a FocusTraversalPolicy is involed
(f. e. the user press TAB to focus next component).
sun.awt.RequestFocusController#acceptRequestFocus is invoked in both cases.
Attached a demo to reproduce it:
- if TextField is focused by a mouse click "requestFocus" and "acceptRequestFocus" is logged
- if TextField is focused by TAB only "acceptRequestFocus" is logged
Some more notes about the test program:
ComponentAccessorDelegator delegates all calls to the original ComponentAccessor implementation.
Only ComponentAccessor#setRequestFocusController is changed to keep our RequestFocusController implementation.
JUSTIFICATION :
We can not remove our validation and focus management because it is tightly integrated into our RIA.
The code run within a JVM which is started by the Oracle Java WebStart.
So we can not "simple" workaround it.
We have over 40'000 customers which use our RIA every day and we don't plan to develop and ship a custom WebStart with a OpenJDK fork.
---------- BEGIN SOURCE ----------
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.CausedFocusEvent;
import sun.awt.RequestFocusController;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.peer.ComponentPeer;
import java.security.AccessControlContext;
public class RequestFocusDemo {
public static void main(String[] args) {
RequestFocusController ourRequestFocusController = new RequestFocusController() {
@Override
public boolean acceptRequestFocus(final Component component, final Component component1, final boolean b, final boolean b1, final CausedFocusEvent.Cause cause) {
System.out.println("acceptRequestFocus");
return true;
}
};
AWTAccessor.setComponentAccessor(new ComponentAccessorDelegator(AWTAccessor.getComponentAccessor()) {
@Override
public void setRequestFocusController(final RequestFocusController requestFocusController) {
super.setRequestFocusController(ourRequestFocusController); // disallow further changes
}
});
AWTAccessor.getComponentAccessor().setRequestFocusController(ourRequestFocusController);
JPanel panel = new JPanel();
BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.setLayout(layout);
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
JFrame frame = new JFrame();
frame.getContentPane().add(panel);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
static class TextFieldImpl extends JTextField {
TextFieldImpl() {
}
@Override
public void requestFocus() {
System.out.println("requestFocus");
super.requestFocus();
}
@Override
public boolean requestFocus(boolean temporary) {
System.out.println("requestFocus(boolean)");
return super.requestFocus(temporary);
}
@Override
public boolean requestFocusInWindow() {
System.out.println("requestFocusInWindow");
return super.requestFocusInWindow();
}
@Override
protected boolean requestFocusInWindow(boolean temporary) {
System.out.println("requestFocusInWindow(boolean)");
return super.requestFocusInWindow(temporary);
}
}
static class ComponentAccessorDelegator implements AWTAccessor.ComponentAccessor {
private AWTAccessor.ComponentAccessor delegation;
private ComponentAccessorDelegator(AWTAccessor.ComponentAccessor delegation) {
this.delegation = delegation;
}
@Override
public void setBackgroundEraseDisabled(Component component, boolean b) {
delegation.setBackgroundEraseDisabled(component, b);
}
@Override
public boolean getBackgroundEraseDisabled(Component component) {
return delegation.getBackgroundEraseDisabled(component);
}
@Override
public Rectangle getBounds(Component component) {
return delegation.getBounds(component);
}
@Override
public void setMixingCutoutShape(Component component, Shape shape) {
delegation.setMixingCutoutShape(component, shape);
}
@Override
public void setGraphicsConfiguration(Component component, GraphicsConfiguration graphicsConfiguration) {
delegation.setGraphicsConfiguration(component, graphicsConfiguration);
}
@Override
public boolean requestFocus(Component component, CausedFocusEvent.Cause cause) {
return delegation.requestFocus(component, cause);
}
@Override
public boolean canBeFocusOwner(Component component) {
return delegation.canBeFocusOwner(component);
}
@Override
public boolean isVisible(Component component) {
return delegation.isVisible(component);
}
@Override
public void setRequestFocusController(RequestFocusController requestFocusController) {
delegation.setRequestFocusController(requestFocusController);
}
@Override
public AppContext getAppContext(Component component) {
return delegation.getAppContext(component);
}
@Override
public void setAppContext(Component component, AppContext appContext) {
delegation.setAppContext(component, appContext);
}
@Override
public Container getParent(Component component) {
return delegation.getParent(component);
}
@Override
public void setParent(Component component, Container container) {
delegation.setParent(component, container);
}
@Override
public void setSize(Component component, int i, int i1) {
delegation.setSize(component, i, i1);
}
@Override
public Point getLocation(Component component) {
return delegation.getLocation(component);
}
@Override
public void setLocation(Component component, int i, int i1) {
delegation.setLocation(component, i, i1);
}
@Override
public boolean isEnabled(Component component) {
return delegation.isEnabled(component);
}
@Override
public boolean isDisplayable(Component component) {
return delegation.isDisplayable(component);
}
@Override
public Cursor getCursor(Component component) {
return delegation.getCursor(component);
}
@Override
public ComponentPeer getPeer(Component component) {
return delegation.getPeer(component);
}
@Override
public void setPeer(Component component, ComponentPeer componentPeer) {
delegation.setPeer(component, componentPeer);
}
@Override
public boolean isLightweight(Component component) {
return delegation.isLightweight(component);
}
@Override
public boolean getIgnoreRepaint(Component component) {
return delegation.getIgnoreRepaint(component);
}
@Override
public int getWidth(Component component) {
return delegation.getWidth(component);
}
@Override
public int getHeight(Component component) {
return delegation.getHeight(component);
}
@Override
public int getX(Component component) {
return delegation.getX(component);
}
@Override
public int getY(Component component) {
return delegation.getY(component);
}
@Override
public Color getForeground(Component component) {
return delegation.getForeground(component);
}
@Override
public Color getBackground(Component component) {
return delegation.getBackground(component);
}
@Override
public void setBackground(Component component, Color color) {
delegation.setBackground(component, color);
}
@Override
public Font getFont(Component component) {
return delegation.getFont(component);
}
@Override
public void processEvent(Component component, AWTEvent awtEvent) {
delegation.processEvent(component, awtEvent);
}
@Override
public AccessControlContext getAccessControlContext(Component component) {
return delegation.getAccessControlContext(component);
}
@Override
public void revalidateSynchronously(Component component) {
delegation.revalidateSynchronously(component);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use internal APIs.
Jigsaw will disallow access to internal packages.
We have written a complex validation and focus management implementation for our closed-source RIA and maintain it since JRE 1.4.
For this we use some internal API. We have a custom implementation of these interfaces:
sun.awt.RequestFocusController
sun.awt.AWTAccessor.ComponentAccessor
And use this getter and setter:
sun.awt.AWTAccessor#setComponentAccessor(AWTAccessor.ComponentAccessor)
sun.awt.AWTAccessor#getComponentAccessor()
sun.awt.AWTAccessor.ComponentAccessor#setRequestFocusController(RequestFocusController)
Please make this API public.
Basically we need to detect and intercept focus changes.
java.awt.Component allows to override this methods:
requestFocus()
requestFocus(boolean)
requestFocusInWindow()
requestFocusInWindow(boolean)
requestFocus() is only invoked when the component is focused by a mouse click.
But no requestFocus* method is invoked when a FocusTraversalPolicy is involed
(f. e. the user press TAB to focus next component).
sun.awt.RequestFocusController#acceptRequestFocus is invoked in both cases.
Attached a demo to reproduce it:
- if TextField is focused by a mouse click "requestFocus" and "acceptRequestFocus" is logged
- if TextField is focused by TAB only "acceptRequestFocus" is logged
Some more notes about the test program:
ComponentAccessorDelegator delegates all calls to the original ComponentAccessor implementation.
Only ComponentAccessor#setRequestFocusController is changed to keep our RequestFocusController implementation.
JUSTIFICATION :
We can not remove our validation and focus management because it is tightly integrated into our RIA.
The code run within a JVM which is started by the Oracle Java WebStart.
So we can not "simple" workaround it.
We have over 40'000 customers which use our RIA every day and we don't plan to develop and ship a custom WebStart with a OpenJDK fork.
---------- BEGIN SOURCE ----------
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.CausedFocusEvent;
import sun.awt.RequestFocusController;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.peer.ComponentPeer;
import java.security.AccessControlContext;
public class RequestFocusDemo {
public static void main(String[] args) {
RequestFocusController ourRequestFocusController = new RequestFocusController() {
@Override
public boolean acceptRequestFocus(final Component component, final Component component1, final boolean b, final boolean b1, final CausedFocusEvent.Cause cause) {
System.out.println("acceptRequestFocus");
return true;
}
};
AWTAccessor.setComponentAccessor(new ComponentAccessorDelegator(AWTAccessor.getComponentAccessor()) {
@Override
public void setRequestFocusController(final RequestFocusController requestFocusController) {
super.setRequestFocusController(ourRequestFocusController); // disallow further changes
}
});
AWTAccessor.getComponentAccessor().setRequestFocusController(ourRequestFocusController);
JPanel panel = new JPanel();
BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.setLayout(layout);
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
panel.add(new TextFieldImpl());
JFrame frame = new JFrame();
frame.getContentPane().add(panel);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
static class TextFieldImpl extends JTextField {
TextFieldImpl() {
}
@Override
public void requestFocus() {
System.out.println("requestFocus");
super.requestFocus();
}
@Override
public boolean requestFocus(boolean temporary) {
System.out.println("requestFocus(boolean)");
return super.requestFocus(temporary);
}
@Override
public boolean requestFocusInWindow() {
System.out.println("requestFocusInWindow");
return super.requestFocusInWindow();
}
@Override
protected boolean requestFocusInWindow(boolean temporary) {
System.out.println("requestFocusInWindow(boolean)");
return super.requestFocusInWindow(temporary);
}
}
static class ComponentAccessorDelegator implements AWTAccessor.ComponentAccessor {
private AWTAccessor.ComponentAccessor delegation;
private ComponentAccessorDelegator(AWTAccessor.ComponentAccessor delegation) {
this.delegation = delegation;
}
@Override
public void setBackgroundEraseDisabled(Component component, boolean b) {
delegation.setBackgroundEraseDisabled(component, b);
}
@Override
public boolean getBackgroundEraseDisabled(Component component) {
return delegation.getBackgroundEraseDisabled(component);
}
@Override
public Rectangle getBounds(Component component) {
return delegation.getBounds(component);
}
@Override
public void setMixingCutoutShape(Component component, Shape shape) {
delegation.setMixingCutoutShape(component, shape);
}
@Override
public void setGraphicsConfiguration(Component component, GraphicsConfiguration graphicsConfiguration) {
delegation.setGraphicsConfiguration(component, graphicsConfiguration);
}
@Override
public boolean requestFocus(Component component, CausedFocusEvent.Cause cause) {
return delegation.requestFocus(component, cause);
}
@Override
public boolean canBeFocusOwner(Component component) {
return delegation.canBeFocusOwner(component);
}
@Override
public boolean isVisible(Component component) {
return delegation.isVisible(component);
}
@Override
public void setRequestFocusController(RequestFocusController requestFocusController) {
delegation.setRequestFocusController(requestFocusController);
}
@Override
public AppContext getAppContext(Component component) {
return delegation.getAppContext(component);
}
@Override
public void setAppContext(Component component, AppContext appContext) {
delegation.setAppContext(component, appContext);
}
@Override
public Container getParent(Component component) {
return delegation.getParent(component);
}
@Override
public void setParent(Component component, Container container) {
delegation.setParent(component, container);
}
@Override
public void setSize(Component component, int i, int i1) {
delegation.setSize(component, i, i1);
}
@Override
public Point getLocation(Component component) {
return delegation.getLocation(component);
}
@Override
public void setLocation(Component component, int i, int i1) {
delegation.setLocation(component, i, i1);
}
@Override
public boolean isEnabled(Component component) {
return delegation.isEnabled(component);
}
@Override
public boolean isDisplayable(Component component) {
return delegation.isDisplayable(component);
}
@Override
public Cursor getCursor(Component component) {
return delegation.getCursor(component);
}
@Override
public ComponentPeer getPeer(Component component) {
return delegation.getPeer(component);
}
@Override
public void setPeer(Component component, ComponentPeer componentPeer) {
delegation.setPeer(component, componentPeer);
}
@Override
public boolean isLightweight(Component component) {
return delegation.isLightweight(component);
}
@Override
public boolean getIgnoreRepaint(Component component) {
return delegation.getIgnoreRepaint(component);
}
@Override
public int getWidth(Component component) {
return delegation.getWidth(component);
}
@Override
public int getHeight(Component component) {
return delegation.getHeight(component);
}
@Override
public int getX(Component component) {
return delegation.getX(component);
}
@Override
public int getY(Component component) {
return delegation.getY(component);
}
@Override
public Color getForeground(Component component) {
return delegation.getForeground(component);
}
@Override
public Color getBackground(Component component) {
return delegation.getBackground(component);
}
@Override
public void setBackground(Component component, Color color) {
delegation.setBackground(component, color);
}
@Override
public Font getFont(Component component) {
return delegation.getFont(component);
}
@Override
public void processEvent(Component component, AWTEvent awtEvent) {
delegation.processEvent(component, awtEvent);
}
@Override
public AccessControlContext getAccessControlContext(Component component) {
return delegation.getAccessControlContext(component);
}
@Override
public void revalidateSynchronously(Component component) {
delegation.revalidateSynchronously(component);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Use internal APIs.
- relates to
-
JDK-8154431 Allow source and target based validation for the focus transfer between two JComponents
- Resolved