Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 257094 - [Markers] NPE in MarkerHelpRegistry
Summary: [Markers] NPE in MarkerHelpRegistry
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.5 M6   Edit
Assignee: Hitesh CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-12-01 11:44 EST by Tim deBoer CLA
Modified: 2009-06-01 14:29 EDT (History)
2 users (show)

See Also:


Attachments
Check Null (1.96 KB, patch)
2009-01-20 05:19 EST, Hitesh CLA
emoffatt: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tim deBoer CLA 2008-12-01 11:44:47 EST
We've been analyzing user .log files to root out common problems and clean up unnecessary errors. Since this is done offline I have no further info on what the user(s) were doing at the time, but the following NPE was hit 77 times by our users during the month of October:

!ENTRY org.eclipse.jface 4 2
    !MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.jface".
    !STACK 0
    java.lang.NullPointerException
    	at org.eclipse.ui.internal.ide.registry.MarkerHelpRegistry.hasResolution(Unknown Source)
    	at org.eclipse.ui.internal.ide.registry.MarkerHelpRegistry.hasResolutions(Unknown Source)
    	at org.eclipse.ui.views.markers.MarkerField.annotateImage(Unknown Source)
    	at org.eclipse.ui.internal.views.markers.MarkerProblemSeverityAndMessageField.update(Unknown Source)
    	at org.eclipse.ui.internal.views.markers.MarkerColumnLabelProvider.update(Unknown Source)
    	at org.eclipse.jface.viewers.ViewerColumn.refresh(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.doUpdateItem(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer$UpdateItemSafeRunnable.run(Unknown Source)
    	at org.eclipse.core.runtime.SafeRunner.run(Unknown Source)
    	at org.eclipse.core.runtime.Platform.run(Unknown Source)
    	at org.eclipse.ui.internal.JFaceUtil$1.run(Unknown Source)
    	at org.eclipse.jface.util.SafeRunnable.run(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.doUpdateItem(Unknown Source)
    	at org.eclipse.jface.viewers.StructuredViewer$UpdateItemSafeRunnable.run(Unknown Source)
    	at org.eclipse.core.runtime.SafeRunner.run(Unknown Source)
    	at org.eclipse.core.runtime.Platform.run(Unknown Source)
    	at org.eclipse.ui.internal.JFaceUtil$1.run(Unknown Source)
    	at org.eclipse.jface.util.SafeRunnable.run(Unknown Source)
    	at org.eclipse.jface.viewers.StructuredViewer.updateItem(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.createTreeItem(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer$1.run(Unknown Source)
    	at org.eclipse.swt.custom.BusyIndicator.showWhile(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.createChildren(Unknown Source)
    	at org.eclipse.jface.viewers.TreeViewer.createChildren(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.internalExpandToLevel(Unknown Source)
    	at org.eclipse.jface.viewers.AbstractTreeViewer.expandToLevel(Unknown Source)
    	at org.eclipse.ui.internal.views.markers.MarkersTreeViewer.expandToLevel(Unknown Source)
    	at org.eclipse.ui.internal.views.markers.ExtendedMarkersView.reexpandCategories(Unknown Source)
    	at org.eclipse.ui.internal.views.markers.ExtendedMarkersView$12.runInUIThread(Unknown Source)
    	at org.eclipse.ui.progress.UIJob$1.run(Unknown Source)
    	at org.eclipse.swt.widgets.RunnableLock.run(Unknown Source)
    	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Unknown Source)
    	at org.eclipse.swt.widgets.Display.runAsyncMessages(Unknown Source)
    	at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source)
    	at org.eclipse.ui.internal.Workbench.runEventLoop(Unknown Source)
    	at org.eclipse.ui.internal.Workbench.runUI(Unknown Source)
    	at org.eclipse.ui.internal.Workbench.access$4(Unknown Source)
    	at org.eclipse.ui.internal.Workbench$5.run(Unknown Source)
    	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Unknown Source)
    	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Unknown Source)
    	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(Unknown Source)
    	at org.eclipse.ui.internal.ide.application.IDEApplication.start(Unknown Source)
    	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(Unknown Source)
    	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(Unknown Source)
    	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(Unknown Source)
    	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(Unknown Source)
    	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(Unknown Source)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Unknown Source)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at org.eclipse.equinox.launcher.Main.invokeFramework(Unknown Source)
    	at org.eclipse.equinox.launcher.Main.basicRun(Unknown Source)
    	at org.eclipse.equinox.launcher.Main.run(Unknown Source)
    	at org.eclipse.equinox.launcher.Main.main(Unknown Source)
Comment 1 Hitesh CLA 2008-12-02 02:58:31 EST
if (generator != null) {
	if (generator instanceof IMarkerResolutionGenerator2) {
		if (((IMarkerResolutionGenerator2) generator)
			.hasResolutions(marker)) {
				return true;
		}
	} else {
	IMarkerResolution[] resolutions = generator
						.getResolutions(marker);
		if (resolutions.length > 0) {
			// there is at least one resolution
				return true;
		}
	}
}

Although, looking at the method raises suspicions on the generator returning a null for resolutions rather than an empty array (as the JavaDoc says) , this would need more info and steps to reproduce.

If the particular generator is actually returning null, then that needs to be fixed.
Comment 2 Hitesh CLA 2009-01-16 02:28:20 EST
The method is clean and sound enough against NPEs. I am closing this. Please feel free to reopen if you have further information on the bug.
Comment 3 Tim deBoer CLA 2009-01-16 09:51:19 EST
The previous comment does not make any sense. The method is not sound enough against NPEs, as evidenced by the amount of NPEs that are occurring in the field.

I agree it's likely a generator not following the spec that is causing the failure, but it's causing a failure in *this* method, with no tracing to determine what else might be at fault. This method needs to be robust enough to handle the problem, either by adding a simple "resolutions != null", or code to catch and log/trace which generator is at fault.
Comment 4 Hitesh CLA 2009-01-16 11:17:08 EST
(In reply to comment #3)

I am not convinced to adding a null check there. When a method is expected (spec'd) not to return a null, why should the caller check for it. 
If I were to follow that philosophy as a way of making code robust, I would have to be checking for nulls and logging NPEs after almost every method call. 

I'll leave this open, but I am afraid with the limited info this will see little progress.
Comment 5 Tim deBoer CLA 2009-01-16 15:35:47 EST
FWIW, I checked and 8 additional users of our adopter product reported this during Nov.

This method is clearly failing, so how much more info do you need? I definitely do not expect null checks on every use of a variable, but it's a common Eclipse practice (and good defensive programming technique) to be defensive when calling extension points and other external components. 

Yes, the method is spec'd to not return null. However we're trusting some other team to get it right, and if they accidentally return the wrong value this method throws an uncaught exception. Worse, it fails in such a way that this method looks like the culprit and there is no way to track down who actually caused the problem. Until we improve this, people will continue to see failures that point back to org.eclipse.ui.
Comment 6 Paul Webster CLA 2009-01-18 13:00:18 EST
(In reply to comment #5)
> Yes, the method is spec'd to not return null. However we're trusting some other
> team to get it right, and if they accidentally return the wrong value this
> method throws an uncaught exception. Worse, it fails in such a way that this
> method looks like the culprit and there is no way to track down who actually
> caused the problem. Until we improve this, people will continue to see failures
> that point back to org.eclipse.ui.

I agree, it's worth changing the method to include a useful log:

if (resolutions==null) {
  // call the status manager with the failure, something like:
  "Failure in " + element.createExecutableExtension(ATT_CLASS) + 
  " from plugin " + element.getContributor().getName() 
  + ": getResolutions() return null"
  return false;
}

PW
Comment 7 Eric Moffatt CLA 2009-01-18 13:12:56 EST
Paul, shouldn't that be "returns null" or "returned null" ?

I might say "must not return null" since it more clearly says that someone is breaking the API contract...
Comment 8 Paul Webster CLA 2009-01-18 13:14:54 EST
(In reply to comment #7)
> Paul, shouldn't that be "returns null" or "returned null" ?
> 
> I might say "must not return null" since it more clearly says that someone is
> breaking the API contract...

The part of interest to me is that you can print out the offending class and the bundle it came from.  But I didn't want to suggest "Some clown returned null ..." :-) 

PW
Comment 9 Hitesh CLA 2009-01-19 07:28:38 EST
(In reply to comment #6 and comment #7)

Note that this is not the only place where we call IMarkerResolutionGenerator, and also not the only method we call that might return null. The null checking and logging would have to be added in all the other places as well.

This seems to suggest that null-checking and logging be done every time we call a 'non-null-returning' method from an extension point API (?) - if so, is this really that common a practice :)
Comment 10 Paul Webster CLA 2009-01-19 07:37:00 EST
(In reply to comment #9)
> (In reply to comment #6 and comment #7)
> 
> Note that this is not the only place where we call IMarkerResolutionGenerator,
> and also not the only method we call that might return null. The null checking
> and logging would have to be added in all the other places as well.

The NPE protection is actually a side-effect of adding logging to track down the culprit ... :-)

PW
Comment 11 Hitesh CLA 2009-01-19 08:09:02 EST
(In reply to comment #10)
> (In reply to comment #9)
> > (In reply to comment #6 and comment #7)
> > 
> > Note that this is not the only place where we call IMarkerResolutionGenerator,
> > and also not the only method we call that might return null. The null checking
> > and logging would have to be added in all the other places as well.
> 
> The NPE protection is actually a side-effect of adding logging to track down
> the culprit ... :-)
> 
> PW
> 

We could have many culprits, in many different places ,striking in many different ways, and at different times :-) Do we chase all of them ;-)

What if another implementation returned a null for a different method ( also spec'd non-null-return ) at a different place, in a different way...

I'll post a patch if this is absolutely needed.

PS: This looks like a bug that should be discovered during development rather than being caught later.
Comment 12 Hitesh CLA 2009-01-20 05:19:11 EST
Created attachment 123058 [details]
Check Null
Comment 13 Eric Moffatt CLA 2009-01-20 16:06:57 EST
Committed in >20090120. Applied the patch...Thanks Hitesh!
Comment 14 Hitesh CLA 2009-05-01 03:52:05 EDT
Verified in I20090429-0800.