Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradle and Spring-bootRun can not find my class

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.

like image 395
Grim Avatar asked Jun 05 '20 10:06

Grim


People also ask

What is bootRun in Gradle?

The Spring Boot gradle plugin provides the bootRun task that allows a developer to start the application in a “developer mode” without first building a JAR file and then starting this JAR file. Thus, it's a quick way to test the latest changes you made to the codebase.

Can Gradle be used with spring boot?

Introduction. The Spring Boot Gradle Plugin provides Spring Boot support in Gradle. It allows you to package executable jar or war archives, run Spring Boot applications, and use the dependency management provided by spring-boot-dependencies . Spring Boot's Gradle plugin requires Gradle 6.8, 6.9, or 7.

How to set main class in Spring Boot Gradle?

If we're using the Spring Boot Gradle plugin, there are a few configurations inherited from org.springframework.boot where we could specify our main class. In the project's Gradle file, mainClassName can be defined within springBoot configuration block. This change made here is picked up by bootRun and bootJar task:

What does the Gradle plugin do?

The gradle plugin extends our build script’s DSL with elements from our spring boot plugin’s spring boot settings. Set the appropriate properties of the spring boot plugin is very important.

How to set the main class of a class in Spring Boot?

For example, let's set the main class by using the mainClassName property: Alternatively, we can use use the same property from the Spring Boot DSL: 3.2. Spring Boot 1.x With Spring Boot 1.x, bootRepackage is responsible for creating the executable archive (jar or war file depending on the configuration.

How to configure bootrun task in Spring Boot?

The bootRun task can be simply configured in build.gradle. For example, we can define the main class: 5. Relation With Other Plugins 5.1. Dependency Management Plugin For Spring Boot 1.x, it used to apply the dependency management plugin automatically.


2 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

like image 178
Franco Rondini Avatar answered Nov 10 '22 13:11

Franco Rondini


What you need is Gradle's composite build. See my POC project. The advanced project includes the classic project by this line

The advanced/App.java calls classic.App.main, And the classic.App class can load foo.Bar from the advanced project. No need to do a commit to classic project.

Execute "gradlew run" in the advanced folder

to see how it works.

like image 40
Mehmet Sunkur Avatar answered Nov 10 '22 12:11

Mehmet Sunkur