Hi,

Matisse, the NetBeans GUI designer, won't let me add a custom JPanel (or
any kind of custom swing component, really) to a JFrame.  It errors out.
It errors out only when I declare a module-info.java in my project,
which is a Maven-based project.  However, it works just fine when I
remove the module-info.java file from my project.

I'm using this version of NetBeans and the JDK, although I have
reproduced the problem on Windows using OpenJDK 14 as well:

NetBeans 12.1
JDK from AdoptOpenJDK

Product Version: Apache NetBeans IDE 12.1
Java: 15.0.1; OpenJDK 64-Bit Server VM 15.0.1+9
Runtime: OpenJDK Runtime Environment 15.0.1+9
System: Linux version 5.4.7-200.fc31.x86_64 running on amd64; UTF-8; en_US (nb)
User directory: /home/marusich/.netbeans/12.1
Cache directory: /home/marusich/.cache/netbeans/12.1

Here's how to reproduce the issue:

- File > New Project > Java with Maven > Java Application
- Click Next.
- Defaults are fine.  You might need to change the name "mavenproject1"
  to something else if you already have a project with that name.
- Click Finish
- Right click on "com.mycompany.mavenproject1" > New > JPanel Form
- Change the Class Name to: MyJPanel
- Click Finish
- Right click on "com.mycompany.mavenproject1" > New > JFrame Form
- Defaults are fine
- Right click project > build.  It should succeed.
- Open the NewJFrame class.
- Click "Design" to enter the design view
- Now, in Projects window, click on MyJPanel, hold down, and drag it
onto the NewJFrame in the designer.

Note how it works correctly in this case.  there is now a MyJPanel added
to the frame.  Go ahead and delete the MyJPanel you just added.

- Now, right click project > new > Other > Java Module Info
- Click Finish
- Replace the contents of the file, which starts out broken anyway, with
the following:

  module mymodule {
      requires java.base;
      requires java.desktop;
      requires java.logging;

      exports com.mycompany.mavenproject1;
  }

- Save everything.
- Right click on project > clean and build.
- NetBeans might say: "Project mavenproject1 contains module-info.java,
but modules need maven-compiler-plugin >= 3.6.  Update your pom.xml?"
Click Yes.
- It might stop there after updating the pom.xml and not do anything
else.  That's OK.  Save everything again, right click on project > clean
and build once more, and make sure it builds successfully.
- Open NewJFrame.
- Click "Design" to enter the design view.
- Now, in Projects window, click on MyJPanel, hold down, and drag it
onto the NewJFrame in the designer.

Observe that in this case, a warning dialog is displayed:

  Warning

  Cannot load component class com.mycompany.mavenproject1.MyJPanel from
  project /home/marusich/NetBeansProjects/mavenproject1.

  The class could not be found.  Note the class must be compiled and
  must be part of the sources or dependencies of the project where the
  target GUI form belongs to.

This is not what I expected.  I expected NetBeans to add a MyJPanel into
the NewJFrame, just like it did before I added module-info.java existed,
and without displaying any warnings.

I grabbed the NetBeans source and followed the instructions at
http://netbeans.apache.org/download/dev/index.html and
https://cwiki.apache.org/confluence/display/NETBEANS/Development+Environment
to build and run a recent version of NetBeans from source (I specified
-Dpermit.jdk9.builds=true, and the precise version is
dev-15a601078cbaa38ba13b77b3de854b8011d71aab), and the problem still
occurred.  I tried debugging the NetBeans source code, but all I could
find was the following information...

In the happy case, when module-info.java does NOT exist, the problem
does NOT occur.  I can see that in the NetBeans source code,
org.netbeans.modules.form.project.ClassPathUtils#loadClass(String,
FileObject) calls Class.forName like so:

  return Class.forName(name, true, getFormClassLoader(fileInProject);

By setting a breakpoint here, I can see that the arguments are as
follows when I drag the MyJPanel onto the frame:

  name = "com.mycompany.mavenproject1.MyJPanel"
  fileInProject = a FileObj for the project's file "NewJFrame.form"

I see that getFormClassLoader returns a (non-null) FormClassLoader.
However, surprisingly, even when I set many breakpoints in
FormClassLoader's methods, the breakpoints are never triggered, but in
spite of this, the Class.forName call successfully returns a Class
object for the com.mycompany.mavenproject1.MyJPanel class.  I tried
looking into the OpenJDK's HotSpot native code for the implementation of
Class.forName0 to understand how that could happen, but I had a hard
time understanding how it successfully returned a Class object for
com.mycompany.mavenproject1.MyJPanel without actually using the supplied
FormClassLoader.  I must be missing something, since I do not understand
how Class.forName0 successfully loads the MyJPanel class in this case.

In the sad path, when module-info.java DOES exist, the problem DOES
occur.  In this case, if you trace the same code starting from the same
breakpoint in ClassPathUtils.loadClass, you'll find that this time, the
methods of the FormClassLoader are actually invoked.  For instance,
FormClassLoader#findClass(String) appears to be the first method invoked
when you drag MyJPanel onto the form in the GUI designer.  However, even
though (and perhaps because) the methods from FormClassLoader are
invoked this time, ultimately the class fails to be loaded - i.e., the
following exception is thrown:

  java.lang.ClassNotFoundException: com.mycompany.mavenproject1.MyJPanel
      at 
org.netbeans.modules.form.project.ProjectClassLoader.findClass(ProjectClassLoader.java:163)
      at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
      at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
      at 
org.netbeans.modules.form.project.FormClassLoader.findClass(FormClassLoader.java:58)
      at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
      at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
      at java.base/java.lang.Class.forName0(Native Method)
      at java.base/java.lang.Class.forName(Class.java:427)
      at 
org.netbeans.modules.form.project.ClassPathUtils.loadClass(ClassPathUtils.java:89)
      at 
org.netbeans.modules.form.MetaComponentCreator$5.run(MetaComponentCreator.java:1425)
      at org.netbeans.modules.form.FormLAF$2.run(FormLAF.java:268)
      at 
org.netbeans.modules.openide.util.NbMutexEventProvider$Event.doEventAccess(NbMutexEventProvider.java:115)
      at 
org.netbeans.modules.openide.util.NbMutexEventProvider$Event.readAccess(NbMutexEventProvider.java:75)
      at 
org.netbeans.modules.openide.util.LazyMutexImplementation.readAccess(LazyMutexImplementation.java:71)
      at org.openide.util.Mutex.readAccess(Mutex.java:225)
      at 
org.netbeans.modules.form.FormLAF.executeWithLookAndFeel(FormLAF.java:251)
      at 
org.netbeans.modules.form.MetaComponentCreator.prepareClass(MetaComponentCreator.java:1421)
      at 
org.netbeans.modules.form.HandleLayer$NewComponentDropListener.dragEnter(HandleLayer.java:3598)
      at java.desktop/java.awt.dnd.DropTarget.dragEnter(DropTarget.java:355)
      at 
java.desktop/sun.awt.dnd.SunDropTargetContextPeer.processEnterMessage(SunDropTargetContextPeer.java:334)
      at 
java.desktop/sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEnterEvent(SunDropTargetContextPeer.java:810)
      at 
java.desktop/sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:778)
      at 
java.desktop/sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:48)
      at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4877)
      at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
      at java.desktop/java.awt.Component.dispatchEvent(Component.java:4844)
      at 
java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4919)
      at 
java.desktop/java.awt.LightweightDispatcher.retargetMouseEnterExit(Container.java:4699)
      at 
java.desktop/java.awt.LightweightDispatcher.trackDropTargetEnterExit(Container.java:4648)
      at 
java.desktop/java.awt.LightweightDispatcher.trackMouseEnterExit(Container.java:4661)
      at 
java.desktop/java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:4614)
      at 
java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4484)
      at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
      at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2764)
      at java.desktop/java.awt.Component.dispatchEvent(Component.java:4844)
      at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
      at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
      at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
      at 
java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
      at 
java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
      at 
java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
      at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
      at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
      at 
java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
      at 
java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
      at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
      at 
org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:136)
      at 
java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
      at 
java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
      at 
java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
      at 
java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
      at 
java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
      at 
java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

I'm not sure how to proceed.  I imagine this might be a module-related
bug, but I'm just not sure what's going on.  I also tried updating the
module-info.java file so that the "mymodule" module is open, but the
problem still occurred.

Any ideas?

-- 
Chris

Attachment: signature.asc
Description: PGP signature

Reply via email to