United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4947550 : Null Pointer in creating Window

Details
Type:
Bug
Submit Date:
2003-11-03
Status:
Resolved
Updated Date:
2004-09-16
Project Name:
JDK
Resolved Date:
2003-12-18
Component:
client-libs
OS:
windows_2000
Sub-Component:
java.awt
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.3.1_09
Fixed Versions:
1.3.1_11 (11)

Related Reports
Backport:
Backport:

Sub Tasks

Description
Customer is seeing a NPE.
Error: java.lang.NullPointerException
        at java.awt.Window.<init>(Window.java:197)
        at java.awt.Frame.<init>(Frame.java:310)
        at java.awt.Frame.<init>(Frame.java:257)
        at com.epiphany.charts.roguewave.ChartFactory.getGIFRepresentation(ChartFactory.java:209)
        at com.epiphany.charts.roguewave.ChartFactory.createChart(ChartFactory.java:271)
        at com.epiphany.charts.CreateChartAction.execute(CreateChartAction.java:111)
        at com.epiphany.server.CommandThread.execute(CommandThread.java:301)
        at com.epiphany.server.CommandThread.runInternal(CommandThread.java:160)
        at com.epiphany.server.EpiThread.run(EpiThread.java:96)
###@###.### 2003-11-03

                                    

Comments
WORK AROUND

As screenBounds is null, here is what customer changed in Window.java to workaround the problem:

The change is in the java.awt.Window class, within the Window(GraphicsConfiguration gc) constructor.
        /* offset the initial location with the original of the screen */
        Rectangle screenBounds = graphicsConfig.getBounds();
        int x = getX() + screenBounds.x;
        int y = getY() + screenBounds.y;
        setLocation(x, y);

becomes
        /* offset the initial location with the original of the screen */
        Rectangle screenBounds = graphicsConfig.getBounds();
        if (screenBounds != null) {
            int x = getX() + screenBounds.x;
            int y = getY() + screenBounds.y;
            setLocation(x, y);
        } else {
            setLocation(getX(), getY());
        }
###@###.### 2003-11-03
                                     
2003-11-03
SUGGESTED FIX

*** /home/bchristi/jano2/bchristi/tiger/webrev/src/windows/native/sun/windows/awt_Win32GraphicsConfig.cpp-      Fri Nov  7 15:26:57 2003
--- awt_Win32GraphicsConfig.cpp Fri Nov  7 15:25:21 2003

*** 79,90 ****
          if( TRUE == ::MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW) ) {
              bounds = env->NewObject(clazz, mid,
                                      rRW.left, rRW.top,
                                      rRW.right - rRW.left,
                                      rRW.bottom - rRW.top);
              if (safe_ExceptionOccurred(env)) {
                  return 0;
              }
          }
-     }
      return bounds;
  }
--- 79,98 ----
          if( TRUE == ::MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW) ) {
              bounds = env->NewObject(clazz, mid,
                                      rRW.left, rRW.top,
                                      rRW.right - rRW.left,
                                      rRW.bottom - rRW.top);
+         }
+         else {
+             // 4910760 - don't return a null bounds, return the bounds of the
+             // primary screen
+             bounds = env->NewObject(clazz, mid,
+                                     0, 0,
+                                     ::GetSystemMetrics(SM_CXSCREEN),
+                                     ::GetSystemMetrics(SM_CYSCREEN));
+         }
          if (safe_ExceptionOccurred(env)) {
              return 0;
          }
      }
      return bounds;
  }

*** /home/bchristi/jano2/bchristi/tiger/webrev/src/windows/native/sun/windows/awt_Win32GraphicsEnv.cpp- Fri Nov  7 15:26:58 2003
--- awt_Win32GraphicsEnv.cpp    Thu Nov  6 18:32:01 2003

*** 96,106 ****
      for (int i = 0; i < awt_numScreens; i++) {
          if (areSameMonitors(mon, getMHNDFromScreen(i))) {
              return i;
          }
      }
!     return -1;
  }
  
  /*
   * Class:     sun_awt_Win32GraphicsEnvironment
   * Method:    initDisplay
--- 96,107 ----
      for (int i = 0; i < awt_numScreens; i++) {
          if (areSameMonitors(mon, getMHNDFromScreen(i))) {
              return i;
          }
      }
!     DTRACE_PRINTLN("getScreenFromMHND(): couldn't find screen, return default");
!     return AwtWin32GraphicsDevice::GetDefaultDeviceIndex();
  }
  
  /*
   * Class:     sun_awt_Win32GraphicsEnvironment
   * Method:    initDisplay

###@###.### 2003-11-25
                                     
2003-11-25
EVALUATION

Reproducible as described.
###@###.### 2003-11-04

Leaving a Java app running, locking the remote machine, disconnecting, and then reconnecting was not a problem with a "normal" app, such as SwingSet or
Java2Demo.  The test case, though, boils down to:

while(weHaven'tDiedYet) {
     Frame f = new Frame();
     f.pack();
     f.dispose();
}

So, even after disconnecting, the test will still (try to) create native backing
windows.

The NPE comes from this swatch of code in Window.java:
         Rectangle screenBounds = graphicsConfig.getBounds();
         Insets screenInsets = getToolkit().getScreenInsets(graphicsConfig);

   --->  int x = getX() + screenBounds.x + screenInsets.left;
         int y = getY() + screenBounds.y + screenInsets.top;

screenBounds is coming up null, and we NPE on the indicated line.  Here's where
the null comes from:

     Java_sun_awt_Win32GraphicsConfig_getBounds(JNIEnv *env, jobject thisobj,
                                                jint screen) {
...
     jobject bounds = NULL;
...
         if( TRUE == ::MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen),
&rRW) ) {
             bounds = env->NewObject(clazz, mid,
                     rRW.left, rRW.top,
                     rRW.right - rRW.left,
                     rRW.bottom - rRW.top);
...
         }
...    return bounds;
}

::MonitorBounds fails, presumably because our cached MHND is no longer valid
after we disconnect from the remote session.  bounds remains NULL, and is
returned as such.

The customer works around this in their JDK by not using screenBounds if
it's null (see the Work Around).  This may work with 1.3.1_09 and/or 1.4.2_02 (I
didn't try), but I've found that with 1.5 it turns ths exception into a crash. ;(

The crash comes when AwtComponent::SetBackgroundColor() tries to use an array
index of -1.  Observe:

void AwtComponent::SetBackgroundColor(COLORREF c)
{
     int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
     int grayscale = AwtWin32GraphicsDevice::GetGrayness(screen);
     if (grayscale != GS_NOTGRAY) {
...

The basic idea is to get from AwtComponent to grayness via:

HWND->MHND->index into our array of devices->grayness of the device at that index.

The HWND->MHND step is done by the win32 function
::MonitorFromWindow()
called from
AwtWin32GraphicsDevice::DeviceIndexForWindow().

The MHND->index step is done by
getScreenFromMHND()
in
awt_Win32GraphicsEnv.cpp.

So, we get the MHND for our HWND, and look through our cached
AwtWin32GraphicsDevices for that MHND:

int getScreenFromMHND(MHND mon) {
     DASSERT(mon != NULL);
     for (int i = 0; i < awt_numScreens; i++) {
         if (areSameMonitors(mon, getMHNDFromScreen(i))) {
             return i;
         }
     }
     return -1;
}

HOWEVER

When we disconnect from the remote server, ::MonitorFromWindow() starts
returning MHNDs we've never seen.  I assume it does some sort of display change.  At any rate, getScreenFromMHND() nevers finds it, and returns -1.

At first I thought that this problem might be addressed by fixing:
4417798 : Need to track add/remove of monitors on display changes
but from my debugging statements it looks like the crash happens before we see
the WM_DISPLAYCHANGE. event.

My fix has two parts:

1) Return the bounds of the primary monitor from Win32GraphicsConfig.getBounds()
instead of null in the case that ::MonitorBounds() fails.  This prevents the NPE
in Window.java (and anywhere else that might call GC.getBounds()).

2) Return default idx (usually 0) instead of -1 from getScreenFromMHND()

This keeps things running, though in a bit of a weird state, since without
4417798 our AwtWin32GraphicsDevice array is never updated.  But it doesn't NPE
or crash. 
###@###.### 2003-11-25
                                     
2003-11-25
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
1.3.1_11
1.4.2_04
generic
tiger-beta

FIXED IN:
1.3.1_11
1.4.2_04
tiger-beta

INTEGRATED IN:
1.3.1_11
1.4.2_04
tiger-b32
tiger-beta


                                     
2004-09-17



Hardware and Software, Engineered to Work Together