Skip to content

Commit 63cd0a3

Browse files
micklenessprrace
authored andcommitted
4200096: OffScreenImageSource.removeConsumer NullPointerException
Reviewed-by: prr, serb
1 parent db8b3cd commit 63cd0a3

File tree

5 files changed

+394
-2
lines changed

5 files changed

+394
-2
lines changed

src/java.desktop/share/classes/sun/awt/image/OffScreenImageSource.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -201,9 +201,12 @@ private void produce() {
201201
}
202202
}
203203
} catch (NullPointerException e) {
204-
e.printStackTrace();
204+
// If theConsumer is null and we throw a NPE when interacting with it:
205+
// That's OK. That is an expected use case that can happen when an
206+
// ImageConsumer detaches itself from this ImageProducer mid-production.
205207

206208
if (theConsumer != null) {
209+
e.printStackTrace();
207210
theConsumer.imageComplete(ImageConsumer.IMAGEERROR);
208211
}
209212
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.image.BufferedImage;
25+
26+
/**
27+
* @test
28+
* @bug 4200096
29+
* @summary OffScreenImageSource.addConsumer(null) shouldn't throw (or print) a NullPointerException
30+
* @author Jeremy Wood
31+
*/
32+
33+
/**
34+
* This makes sure if OffScreenImageSource#addConsumer(null) is called: we
35+
* treat that as a no-op and return immediately.
36+
* <p>
37+
* This test exists primarily to make sure the resolution to 4200096 does not
38+
* significantly change legacy behavior. Whether or not a NPE is printed to
39+
* System.err is not a hotly contested question, but at one point one of the
40+
* proposed (and rejected) resolutions to 4200096 had the potential to
41+
* throw a NPE when addConsumer(null) was called. That would be a
42+
* significant change that we want to avoid.
43+
* </p>
44+
*/
45+
public class AddNullConsumerTest {
46+
public static void main(String[] args) throws Exception {
47+
try (AutoCloseable setup = bug4200096.setupTest(false)) {
48+
BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
49+
bufferedImage.getSource().addConsumer(null);
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.Image;
25+
import java.awt.image.BufferedImage;
26+
import java.awt.image.ImageObserver;
27+
28+
/**
29+
* @test
30+
* @bug 4200096
31+
* @summary this real-world example detaches an ImageConsumer from an OSIS immediately after it learns the dimensions.
32+
* @author Jeremy Wood
33+
*/
34+
35+
/**
36+
* This adds an ImageObserver that is only interested in identifying the dimensions of an ImageProducer.
37+
* <p>
38+
* Once it has the dimensions: {@link java.awt.image.ImageObserver#imageUpdate(Image, int, int, int, int, int)}
39+
* returns false. This triggers the caller to remove the ImageObserver for us. And removing an ImageObserver
40+
* while an OffScreenImageSource is mid-production triggers the NPE that is JDK-4200096.
41+
* <p>
42+
* The expected behavior is for this method to complete without OffScreenImageSource throwing/catching a NPE.
43+
* <p>
44+
* What's interesting about this test is: we never even explicitly call {@link java.awt.image.ImageProducer#addConsumer(ImageConsumer)}
45+
* or {@link java.awt.image.ImageProducer#removeConsumer(ImageConsumer)} (ImageConsumer)}.
46+
* </p>
47+
*/
48+
public class ImageSizeTest {
49+
public static void main(String[] args) throws Exception {
50+
try (AutoCloseable setup = bug4200096.setupTest(false)) {
51+
Image img = createAbstractImage();
52+
ImageObserver observer = new ImageObserver() {
53+
Integer imageWidth, imageHeight;
54+
@Override
55+
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
56+
if ( (infoflags | ImageObserver.WIDTH) > 0) {
57+
imageWidth = width;
58+
}
59+
if ( (infoflags | ImageObserver.HEIGHT) > 0) {
60+
imageHeight = height;
61+
}
62+
63+
if (imageWidth != null || imageHeight != null)
64+
return false;
65+
return true;
66+
67+
}
68+
};
69+
img.getWidth(observer);
70+
}
71+
}
72+
73+
/**
74+
* This creates a ToolkitImage, so it is not a BufferedImage.
75+
* <p>
76+
* This specific implementation happens to rely on scaling an existing
77+
* BufferedImage, because that seemed like an easy way to avoid bundling a
78+
* JPG/PNG with this unit test. But this return value still happens to be
79+
* a ToolkitImage, which is what a JPG/PNG would also be (when loaded
80+
* via the Toolkit class and not ImageIO).
81+
* </p>
82+
*/
83+
private static Image createAbstractImage() {
84+
BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
85+
Image img = bufferedImage.getScaledInstance(2, 2, Image.SCALE_SMOOTH);
86+
return img;
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.image.BufferedImage;
25+
import java.awt.image.ColorModel;
26+
import java.awt.image.ImageConsumer;
27+
import java.util.Hashtable;
28+
29+
/**
30+
* @test
31+
* @bug 4200096
32+
* @summary This makes sure NPE's that come from the ImageConsumer are still printed to System.err
33+
* @author Jeremy Wood
34+
*/
35+
36+
/**
37+
* This test makes sure that if an ImageConsumer throws a NullPointerException: that NPE
38+
* should be printed to System.err.
39+
* <p>
40+
* Most of the discussion around JDK-4200096 focuses on how we handle NPE's that come from
41+
* OffScreenImageSource itself, but this test checks how we handle NPE's that come from
42+
* the ImageConsumer that's listening to an OffScreenImageSource.
43+
* </p>
44+
* <p>
45+
* This test is enforcing a legacy behavior.
46+
* </p>
47+
*/
48+
public class LegitimateNullPointerTest {
49+
public static void main(String[] args) throws Exception {
50+
try (AutoCloseable setup = bug4200096.setupTest(true)) {
51+
BufferedImage bufferedImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
52+
bufferedImage.getSource().addConsumer(new ImageConsumer() {
53+
54+
@Override
55+
public void setDimensions(int width, int height) {
56+
throw new NullPointerException();
57+
}
58+
59+
@Override
60+
public void setProperties(Hashtable<?, ?> props) {
61+
// intentionally empty
62+
}
63+
64+
@Override
65+
public void setColorModel(ColorModel model) {
66+
// intentionally empty
67+
}
68+
69+
@Override
70+
public void setHints(int hintflags) {
71+
// intentionally empty
72+
}
73+
74+
@Override
75+
public void setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int off, int scansize) {
76+
// intentionally empty
77+
}
78+
79+
@Override
80+
public void setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int off, int scansize) {
81+
// intentionally empty
82+
}
83+
84+
@Override
85+
public void imageComplete(int status) {
86+
// intentionally empty
87+
}
88+
});
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)