IntelliJ Plugin, createContent fails in released Plugin

Answered

I have an IntelliJ plugin that I maintain. I generally have to rebuild it each major release.

I just updated my Idea to 2023.1 and needed to update my plugin compatibility.

I cannot get it to properly initialize my ToolWindow any more. What's more interesting is it works just fine when running in the debugger.

I've tracked it down to this difference (working vs. not working)

public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {

log.info("Create New Instance Of Task Main");
final TaskMain taskMain = new TaskMain(project, toolWindow);

log.info("Getting ContentManager");
final ContentManager contentManager = toolWindow.getContentManager();
log.info("ContentManager: {}", contentManager);

log.info("Getting ContentFactory");
final ContentFactory contentFactory = contentManager.getFactory();
log.info("ContentFactory: {}", contentFactory);

log.info("Fetching Panel [taskMain.getContent()]");
final JPanel panel = taskMain.getContent();
log.info("Panel: {}", panel);

log.info("Create Content from Content Manager");
Content content = null;
try {
content = contentFactory.createContent(panel, null, false);
} catch ( Exception e ) {
log.error(e.getMessage(), e);
}
try {
log.info("Content Component: {}", content.getComponent().getName() );
} catch ( Exception e ) {
log.error(e.getMessage(), e);
}
log.info("Adding Content to Content Manager: {}", content);
contentManager.addContent(content);

log.info("Init");
taskMain.init();

}

When running in the debugger vs. production, I get the following:

Working: gradleTask: runIde

2023-04-08 07:36:27,887 INFO  TaskMainFactory - Create New Instance Of Task Main
2023-04-08 07:36:28,510 INFO  TaskMainFactory - Getting ContentManager
2023-04-08 07:36:28,510 INFO  TaskMainFactory - ContentManager: com.intellij.ui.content.impl.ContentManagerImpl@1aa2a106
2023-04-08 07:36:28,511 INFO  TaskMainFactory - Getting ContentFactory
2023-04-08 07:36:28,512 INFO  TaskMainFactory - ContentFactory: com.intellij.ui.content.ContentFactoryImpl@18906190
+2023-04-08 08:14:05,894 INFO TaskMainFactory - Fetching Panel [taskMain.getContent()]
+2023-04-08 08:14:05,894 INFO TaskMainFactory - Panel: javax.swing.JPanel[,0,0,0x0,invalid,layout=com.intellij.uiDesigner.core.GridLayoutManager,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.TitledBorder@62bf28eb,flags=9,maximumSize=,minimumSize=,preferredSize=]
2023-04-08 07:36:28,512 INFO  TaskMainFactory - Create Content from Content Manager
2023-04-08 07:36:28,513 INFO  TaskMainFactory - Content Component: null
2023-04-08 07:36:28,514 INFO  TaskMainFactory - Adding Content to Content Manager: Content name=null
2023-04-08 07:36:28,520 INFO  TaskMainFactory - Init

Not Working: released:

2023-04-08 07:38:01,111 INFO  TaskMainFactory - Create New Instance Of Task Main
2023-04-08 07:38:01,681 INFO  TaskMainFactory - Getting ContentManager
2023-04-08 07:38:01,682 INFO  TaskMainFactory - ContentManager: com.intellij.ui.content.impl.ContentManagerImpl@4a892ee5
2023-04-08 07:38:01,682 INFO  TaskMainFactory - Getting ContentFactory
2023-04-08 07:38:01,683 INFO  TaskMainFactory - ContentFactory: com.intellij.ui.content.ContentFactoryImpl@4f49d98
+2023-04-08 08:18:07,848 INFO TaskMainFactory - Fetching Panel [taskMain.getContent()]
+2023-04-08 08:18:07,848 INFO TaskMainFactory - Panel: null
2023-04-08 07:38:01,684 INFO  TaskMainFactory - Create Content from Content Manager
2023-04-08 07:38:01,684 ERROR TaskMainFactory - @NotNull method com/intellij/ui/content/impl/ContentImpl.getComponent must not return null
java.lang.IllegalStateException: @NotNull method com/intellij/ui/content/impl/ContentImpl.getComponent must not return null
    at com.intellij.ui.content.impl.ContentImpl.$$$reportNull$$$0(ContentImpl.java)
    at com.intellij.ui.content.impl.ContentImpl.getComponent(ContentImpl.java:56)
    at inc.fluency.TaskMainFactory.createToolWindowContent(TaskMainFactory.java:74)
    at com.intellij.openapi.wm.impl.ToolWindowImpl.createContentIfNeeded(ToolWindowImpl.kt:548)
    at com.intellij.openapi.wm.impl.ToolWindowImpl.scheduleContentInitializationIfNeeded$intellij_platform_ide_impl(ToolWindowImpl.kt:527)
    at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.doShowWindow(ToolWindowManagerImpl.kt:983)
    at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.showToolWindowImpl(ToolWindowManagerImpl.kt:920)
    at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.showToolWindowImpl$default(ToolWindowManagerImpl.kt:906)
    at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.registerToolWindow$intellij_platform_ide_impl(ToolWindowManagerImpl.kt:1094)
    at com.intellij.toolWindow.ToolWindowSetInitializerKt.registerToolWindows(ToolWindowSetInitializer.kt:176)
    at com.intellij.toolWindow.ToolWindowSetInitializerKt.access$registerToolWindows(ToolWindowSetInitializer.kt:1)
    at com.intellij.toolWindow.ToolWindowSetInitializer$createAndLayoutToolWindows$entries$1.invokeSuspend(ToolWindowSetInitializer.kt:125)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)

I've been trying to figure this out for several days now and could just use a pointer to what might be wrong.

IntellijPlugin 1.13.3

CompilerVersion: 231.8109.175 (although I've tried downgrades)

I would be so grateful for any advice.

 

Eric

1
4 comments

Hi Eric,

It seems that the “release” package doesn’t work because of the code instrumentation.

See ContentImpl.getComponent():

public @NotNull JComponent getComponent() {
  return myComponent;
}

It should return @NotNull value, but it probably returns null, and you get the pasted exception. You don’t get this error in runIde, probably because the instrumentation task is disabled.

See https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html (search for the “instrument” keyword).

1

Karol,

Thank you for pointing me in this direction, although I don't understand what's causing the difference in behavior, I appreciate the guidance.

I've disabled the instrumentCode plugin step and still have the same issue.  If I can draw your attention to a step prior to the "getComponent" step, this seems to be the fundamental problem:

+2023-04-08 08:14:05,894 INFO TaskMainFactory - Fetching Panel [taskMain.getContent()]
+2023-04-08 08:14:05,894 INFO TaskMainFactory - Panel: javax.swing.JPanel[,0,0,0x0,invalid,layout=com.intellij.uiDesigner.core.GridLayoutManager,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.TitledBorder@62bf28eb,flags=9,maximumSize=,minimumSize=,preferredSize=]

vs.

+2023-04-08 08:18:07,848 INFO TaskMainFactory - Fetching Panel [taskMain.getContent()]
+2023-04-08 08:18:07,848 INFO TaskMainFactory - Panel: null

I'm unsure why taskMain.getContent() does not return the proper JPanel in the released jar. It's as if the bind to class isn't happening or hasn't happened yet.

What causes the bound control to be set into the TaskMain class?  The designer has it as a bound control, but the class itself seems to have it bound in the debugger, but unbound in release, at least at this point in code execution.

With appreciation,

Eric

0

I think I found my issue and workaround, just not sure when / why the behavior changed.

To package jars, I was creating an uber jar with shadowJar.

That appears to no longer build from the instrumented classes properly.

I'm not sure if the instrumentClasses task changed or what, but if I manually package the uber jar from the instrumentJar task, things seem to be working.

0

Eric,

You are on the right track, there is an issue with the panel creation. This happens in the constructor via injected UIDesigner code that instantiates and sets their bound fields to the created components. Since this only happens when not debugging it is going to be a PITA to figure out.

If you add a dummy widget that is marked Custom Create and add custom creation method, then you can add traces to the custom create method and to your TaskMain constructor. If you don't access the dummy custom created widget bound field, then you don't need to actually create it in the custom create method, it will be null.

At the top of the TaskMain constructor add a log, and at the top of the custom create method do the same. 

When the plugin runs, you should see the custom creation log entry before the constructor log entry. If you don't see the custom creation log, that means there is a problem with the UIDesigner form injected code or something causes it to fail silently.

Good luck, and please post an update when you figure it out or get more information about the conditions that cause it.

1

Please sign in to leave a comment.