JDK-4294758 : BoxLayout causes java.lang.NullPointerException and applet crashes
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.1.8,1.2.2,1.3.0,6u14
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,solaris_8,windows_98
  • CPU: x86,sparc
  • Submitted: 1999-11-25
  • Updated: 2010-02-22
  • Resolved: 2000-12-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other Other JDK 6
1.3.0_03 03Fixed 1.3.1_01Fixed 1.4.0Fixed 6-poolResolved
Related Reports
Relates :  
Description

Name: skT88420			Date: 11/25/99


java version "1.2.2"
Classic VM (build JDK-1.2.2-001, native threads, symcjit)


1) Run the applet using Appletviewer many times before you see a
NullPointerException. You may have to run it 10-20 times.

3) java.lang.NullPointerException:
        at javax.swing.BoxLayout.checkRequests(BoxLayout.java:362)
        at javax.swing.BoxLayout.layoutContainer(BoxLayout.java:296)
        at java.awt.Container.layout(Container.java:494)
        at java.awt.Container.doLayout(Container.java:484)
        at java.awt.Container.validateTree(Container.java:553)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validateTree(Container.java:560)
        at java.awt.Container.validate(Container.java:535)
        at sun.applet.AppletPanel.run(AppletPanel.java:336)
        at java.lang.Thread.run(Thread.java:479)

5) This problem even appears on windows95 but less frequently

2) html code for applet
<html>
<head>
<title>Swing Applet</title>
</head>

<body>
<APPLET code="StreamPPApplet.class" width=600 height=500> </APPLET>
</body>
</html>

2) Java Code for the applet. I couldn't reproduce with a smaller code

	Code attached to the bug report.

(Review ID: 98298) 
======================================================================

This bug renders the SAP GUI for Java more or less useless for end users.
The box layout is used over and over. Not using it is no option.
Please help. Bugs shows up as well in JDK 1.2.2_06 production release

Stefan Schneider, MDE

stefan.schneider@eng 2000-09-21


The problem arises from the class BoxLayout.
Some member variables are not MT safe and
they are being used (to be more precise misused)
in different threads at a time.

The problem occures in JDK 1.1.8 1.2.2 and 1.3
on all platforms.

The relevant implementation of the class as is
involves the following member variables
xChildren, yChildren

and the methods invalidateLayout and checkRequests.

The crashes may happen for the following reason:
invalidateLayout() may be called from a different thread
while checkRequests() is processed by a another thread.

The method invalidateLayout() is fairly robust since the
only thing is does is setting xChildren and yChildren to null.
This behaviour is safe and stable but fatal for the concurrent
thread checkRequests()

checkRequest tries to be smart and checks if xChildren and yChildren
may been set to null ( see line 2).
It then recreates the objects in line 6 and 7 .
The troubles start behind line 7 since checkRequest()
is naive enough (von Neumann machine) to assume the the variables
are correctly set. Therefore is uses them without additional checks
for the rest of the method.
It is not aware that invalidateLayout() may interfere through a
different
thread and reset the 2 variables...

Please continue reading below the reference code in order to check
for 2 proposed solutions.

/*
 * @(#)BoxLayout.java   1.24 00/02/02
 ...
 */
 
package javax.swing;

public class BoxLayout implements LayoutManager2, Serializable {

private transient SizeRequirements[] xChildren;
private transient SizeRequirements[] yChildren;
...

    /**
     * Indicates that a child has changed its layout related
information,
     * and thus any cached calculations should be flushed.
     *
     * @param target  the affected container
     *
     * @exception AWTError  if the target isn't the container 
        specified to the
     *                      BoxLayout constructor
     */
    public void invalidateLayout(Container target) {
        checkContainer(target);
        xChildren = null;
        yChildren = null;
        xTotal = null;
        yTotal = null;
    }
...

1.    void checkRequests() {
2.        if (xChildren == null || yChildren == null) {
3.            // The requests have been invalidated... recalculate
4.            // the request information.
5.            int n = target.getComponentCount();
6.            xChildren = new SizeRequirements[n];
7.            yChildren = new SizeRequirements[n];
8.            for (int i = 0; i < n; i++) {
9.                Component c = target.getComponent(i);
10.                if (!c.isVisible()) {
11.                    xChildren[i] = new SizeRequirements(0,0,0,
12.                     c.getAlignmentX());
13.                    yChildren[i] = new SizeRequirements(0,0,0,
14.                     c.getAlignmentY());
15.                    continue;
16.                }
17.                Dimension min = c.getMinimumSize();
18.                Dimension typ = c.getPreferredSize();
19                Dimension max = c.getMaximumSize();
20.                xChildren[i] = new SizeRequirements(min.width,
typ.width, 
21.                                                    max.width, 
22.                                                   
c.getAlignmentX());
23.                yChildren[i] = new SizeRequirements(min.height,
typ.height, 
24.                                                    max.height, 
25.                                                   
c.getAlignmentY());
26.            }
            
            if (axis == X_AXIS) {
                xTotal =
SizeRequirements.getTiledSizeRequirements(xChildren);
                yTotal =
SizeRequirements.getAlignedSizeRequirements(yChildren);
            } else {
                xTotal =
SizeRequirements.getAlignedSizeRequirements(xChildren);
                yTotal =
SizeRequirements.getTiledSizeRequirements(yChildren);
            }
        }
    }
            
    private int axis;
    private Container target;

    private transient SizeRequirements[] xChildren;
    private transient SizeRequirements[] yChildren;
    private transient SizeRequirements xTotal;
    private transient SizeRequirements yTotal;
    
    private transient PrintStream dbg;
}
stefan.schneider@eng 2000-11-07

abhijit.saha@Eng 2001-07-03

updating the bugtraq for bugs integrated in appropriate version of jdk.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.3.0_03 generic merlin-beta FIXED IN: 1.3.0_03 1.3.1_01 merlin-beta INTEGRATED IN: 1.3.0_03 1.3.1_01 merlin-beta VERIFIED IN: 1.3.0_03 1.3.1_01a
14-06-2004

WORK AROUND Name: skT88420 Date: 11/25/99 Not using BoxLayout solves this problem. ======================================================================
11-06-2004

SUGGESTED FIX Proposed solutions: Proposal 1 Synchronize the access to the 2 critical pathes by using xChildren as monitor. public void invalidateLayout(Container target) { checkContainer(target); --> synchronized (xChildren) { xChildren = null; yChildren = null; --> } xTotal = null; yTotal = null; void checkRequests() { if (xChildren == null || yChildren == null) { // The requests have been invalidated... recalculate // the request information. int n = target.getComponentCount(); --> synchronized (xchildren) { xChildren = new SizeRequirements[n]; yChildren = new SizeRequirements[n]; for (int i = 0; i < n; i++) { Component c = target.getComponent(i); if (!c.isVisible()) { xChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentX()); yChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentY()); continue; } Dimension min = c.getMinimumSize(); Dimension typ = c.getPreferredSize(); Dimension max = c.getMaximumSize(); xChildren[i] = new SizeRequirements(min.width, typ.width, max.width, c.getAlignmentX()); yChildren[i] = new SizeRequirements(min.height, typ.height, max.height, c.getAlignmentY()); } if (axis == X_AXIS) { xTotal = SizeRequirements.getTiledSizeRequirements(xChildren); yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren); } else { xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren); yTotal = SizeRequirements.getTiledSizeRequirements(yChildren); } --> } } } Proposal 2: The other alternative would work without locking whereas the critical path wouldn't be fully covered. The idea is to modify checkRequests() is such a way that it would not work on the variables xChildren and yChildren for most of the time. Temporary variables would have to be introduced for the block in question. void checkRequests() { if (xChildren == null || yChildren == null) { // The requests have been invalidated... recalculate // the request information. int n = target.getComponentCount(); --> SizeRequirements[] tmpxChildren = new SizeRequirements[n]; --> SizeRequirements[] tmpyChildren = new SizeRequirements[n]; for (int i = 0; i < n; i++) { Component c = target.getComponent(i); if (!c.isVisible()) { --> tmpxChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentX()); --> tmpyChildren[i] = new SizeRequirements(0,0,0, c.getAlignmentY()); continue; } Dimension min = c.getMinimumSize(); Dimension typ = c.getPreferredSize(); Dimension max = c.getMaximumSize(); --> tmpxChildren[i] = new SizeRequirements(min.width, typ.width, max.width, c.getAlignmentX()); --> tmpyChildren[i] = new SizeRequirements(min.height, typ.height, max.height, c.getAlignmentY()); } if (axis == X_AXIS) { xTotal = --> SizeRequirements.getTiledSizeRequirements(tmpxChildren); yTotal = --> SizeRequirements.getAlignedSizeRequirements(tmpyChildren); } else { xTotal = --> SizeRequirements.getAlignedSizeRequirements(tmpxChildren); yTotal = --> SizeRequirements.getTiledSizeRequirements(tmpyChildren); } --> xChildren=tmpxChildren; // The other thread may still interfere between the line // and the next one ! --> xChildren=tmpxChildren; } } A synchronized block around the assigment of the tmp variables to [xy]children would make it perfect. Well invalidateLayout() would need the synchronized block from proposal 1 as well. Other solutions are thinkable as well..
11-06-2004

EVALUATION BoxLayout wasn't designed with concurrency in mind as Swing has a policy of generally only supporting a single thread (the event thread). We do support asynchronous calls to Container.invalidate() however, which then calls into the layout manager as described. I have therefore added support for a call to invalidateLayout that is asynchronous to the event thread. The suggested fix is inadequate as all of the public methods can potentially cause a problem and the suggested fix leaves xTotal and yTotal completely unprotected. timothy.prinzing@eng 2000-11-30 this problem has also been reported on Kestrel, and escalated by the customer. I have backported the fix, and given him binary relief. He has verified the fix, and i will integrate this into our update releases pat.cashman@Ireland 2001-03-01
01-03-2001