Gradle and Spring-bootRun can not find my class

Viewed 1409

Having two Projects:

  • Classic (Having main class)
  • Advanced

Id like to have the Advanced to have all classes the Classic have. But as I run Advanced/gradle bootRun all the Advanced classes seems to be missing.

Advanced/src/main/java/foo/Bar.java:

package foo;
public class Bar{
   //empty
}

Classic/src/main/java/foo/Classic.java:

package foo;
public class Classic {
  public static void main(){
    try {
        Class.forName("foo.Bar");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
  }
}

Exception:

java.lang.ClassNotFoundException: foo.Bar
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    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:340)
    at foo.Classic.main(Classic.java:14)
java.lang.ClassNotFoundException: foo.Bar
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at org.springframework.boot.devtools.restart.classloader.RestartClassLoader.loadClass(RestartClassLoader.java:144)
    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:340)
    at foo.Classic.main(Classic.java:14)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

I have this in my Advanced/build.gradle:

...
dependencies{
  ...
  implementation project(':classic')
  ...
}
...

Important info from Comments:

Jacob G. I talked for awhile with OP in one of the chat rooms, and I've discovered a few things. When attempting to build the Advanced project with Gradle, it reports 50+ compilations errors regarding package not found:, referring to packages in the Classic project. Also, Classic and Advanced do not share a root project, so I have a feeling that attempting to depend on project(':classic') is futile. OP stated that commits can only be made to Advanced, so it may not be possible to fix the issue at all. For anyone curious, OP is also not using an IDE.

Statement of respect

I made an edit to the question, I added an limitation. It was an very important edit that I can only commit changes in Advanced. Because of the fact that I have no experience in gradle I was not aware of the imporance of this additional information. I feel sorry for the late limitation, feel comfort for the courtesy and hope for understanding.

3 Answers

As far as I could understand from the question we are dealing with a multiple project scenario. In this case the multi project builds documentation tells us that it is necessary to have a settings.gradle in the folder that contains the two projects:

enter image description here

then it is possible to run both the projects without cd change directory into the specific folder , but directly from the multi-project root by command: gradle advanced:bootRun

enter image description here

EDIT according to 20200610 EDIT of the question acknowledging the specification: commits can only be made to the Advanced project.

we can still get a solution but in this scenario ( actually not a gradle multi-project)

  • no need to have a settings.gradle at the parent directory level of Advanced; it satisfy the requirement of not being able to commit outside of Advanced

  • it doesn't matter how it's built the Classic project, we don't care about it since we can't commit on it

  • we can't use in Advanced/build.gradle the implementation project(':classic') as dependency since this works only in real gradle multi-project scenarios ; in here we must use a file dependency or another type of dependecy available for the user's development environment.

enter image description here

In this case it is possible to run the Advanced project by cd Advanced then , from the Advanced directory run th command: gradle bootRun

Limited-commit-to-advanced

why it works ?

..In order to better understand how it works lets's inspect the SystemClassLoader's current paths by adding this lines of code in Advanced/src/main/java/com/example/springboot/Application.java

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader)cl).getURLs();
for(URL url: urls){
    System.out.println(url.getFile());
}

the output is:

<multi-project-root>/Advanced/build/classes/java/main/
<multi-project-root>/Advanced/build/resources/main/
<multi-project-root>/Classic/build/libs/Classic-0.0.1-SNAPSHOT.jar
~/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring
... [suppressed many othe spring devtools and gradle related caches  ]

this allow both the Advanced and the Classic classes to find each others

source code of a proof of concempt

The POC source-code new branch has been updated accordingly

I also had the same project structure as the one you have so by making below-mentioned changes it works fine for me :

  1. Entry in build.gradle file

compile project(':Classic')

  1. Entry in settings.gradle
include ':Classic'
project(':Classic').projectDir = new File(settingsDir, '../../libs/Classic')

I hope this will work for you too and yes don't forget to rebuild your grade project after making these changes.

Related