I'm encountering an issue while implementing a Spring multi-module project and need some help. The goal is to structure the modules using Spring and build each module into a container using Docker.
First, the file structure is as follows:
root-project/
├── module-adjust/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── module-common/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── module-user/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── module-video/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── build.gradle
└── settings.gradle
I've confirmed that the application code with the main method in each module runs correctly and processes requests when executed directly.
root-project/
├── module-adjust/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── module-common/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── module-user/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/main/java/com/sparta/
│ └── **UserApplication.java**
│ └── controller/
│ └── **UserController.java**
├── module-video/
│ ├── Dockerfile
│ ├── build.gradle
│ └── src/
├── build.gradle
└── settings.gradle
UserApplication.java
package com.sparta;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
UserController.java
package com.sparta.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("")
class UserController {
@GetMapping("/test")
public String hello() {
return "Hello, this is Adjustment Controller";
}
}
The issue arises when trying to build this project using Docker.
error at git actions when build docker
And this is long long error log
#45 35.64 * Exception is:
#45 35.64 org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':module-adjust:bootJar'.
#45 35.64 at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
#45 35.64 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
#45 35.64 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
#45 35.64 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
#45 35.64 at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
#45 35.65 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
#45 35.65 at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
#45 35.65 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:331)
#45 35.65 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:318)
#45 35.65 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:314)
#45 35.65 at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:80)
#45 35.65 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:314)
#45 35.65 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
#45 35.65 at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
#45 35.65 at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
#45 35.66 at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
#45 35.66 at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
#45 35.66 Caused by: org.gradle.api.internal.tasks.properties.PropertyEvaluationException: Error while evaluating property 'mainClass' of task ':module-adjust:bootJar'.
#45 35.66 at org.gradle.api.internal.tasks.properties.InputParameterUtils.prepareInputParameterValue(InputParameterUtils.java:32)
#45 35.66 at org.gradle.api.internal.tasks.execution.TaskExecution.lambda$visitRegularInputs$1(TaskExecution.java:317)
#45 35.66 at org.gradle.internal.execution.impl.DefaultInputFingerprinter$InputCollectingVisitor.visitInputProperty(DefaultInputFingerprinter.java:103)
#45 35.66 at org.gradle.api.internal.tasks.execution.TaskExecution.visitRegularInputs(TaskExecution.java:315)
#45 35.66 at org.gradle.internal.execution.impl.DefaultInputFingerprinter.fingerprintInputProperties(DefaultInputFingerprinter.java:63)
#45 35.66 at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.captureExecutionStateWithOutputs(AbstractCaptureStateBeforeExecutionStep.java:109)
#45 35.66 at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.lambda$captureExecutionState$0(AbstractCaptureStateBeforeExecutionStep.java:74)
#45 35.66 at org.gradle.internal.execution.steps.BuildOperationStep$1.call(BuildOperationStep.java:37)
#45 35.66 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
#45 35.67 at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
#45 35.67 at org.gradle.internal.execution.steps.BuildOperationStep.operation(BuildOperationStep.java:34)
#45 35.68 at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.captureExecutionState(AbstractCaptureStateBeforeExecutionStep.java:69)
#45 35.68 at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:62)
#45 35.68 at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:43)
#45 35.68 at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:125)
#45 35.68 at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:61)
#45 35.68 at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:36)
#45 35.68 at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
#45 35.68 at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
#45 35.68 at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
#45 35.68 at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
#45 35.68 at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
#45 35.68 at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
#45 35.68 at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:289)
#45 35.68 at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
#45 35.68 at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
#45 35.69 at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
#45 35.69 at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
#45 35.69 at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
#45 35.69 at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
#45 35.69 at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
#45 35.69 at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
#45 35.69 at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
#45 35.69 at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:48)
#45 35.69 at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:35)
#45 35.69 at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:61)
#45 35.69 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:127)
#45 35.69 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:116)
#45 35.69 at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
#45 35.69 at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
#45 35.69 at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
#45 35.69 at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
#45 35.69 at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
#45 35.69 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
#45 35.69 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
#45 35.69 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
#45 35.69 at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
#45 35.69 at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
#45 35.69 at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
#45 35.69 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:331)
#45 35.69 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:318)
#45 35.70 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:314)
#45 35.70 at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:80)
#45 35.70 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:314)
#45 35.70 at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
#45 35.70 at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:463)
#45 35.70 at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:380)
#45 35.70 at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
#45 35.70 at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
#45 35.70 Caused by: org.gradle.api.internal.provider.AbstractProperty$PropertyQueryException: Failed to calculate the value of task ':module-adjust:bootJar' property 'mainClass'.
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.finalizeNow(AbstractProperty.java:284)
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.lambda$beforeRead$0(AbstractProperty.java:276)
#45 35.70 at org.gradle.api.internal.provider.ValueState.finalizeOnReadIfNeeded(ValueState.java:137)
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.beforeRead(AbstractProperty.java:276)
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.beforeRead(AbstractProperty.java:268)
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.calculateOwnValue(AbstractProperty.java:162)
#45 35.70 at org.gradle.api.internal.provider.AbstractMinimalProvider.getOrNull(AbstractMinimalProvider.java:105)
#45 35.70 at org.gradle.api.internal.provider.ProviderResolutionStrategy$1.resolve(ProviderResolutionStrategy.java:27)
#45 35.70 at org.gradle.util.internal.DeferredUtil.unpack(DeferredUtil.java:59)
#45 35.70 at org.gradle.util.internal.DeferredUtil.unpackOrNull(DeferredUtil.java:49)
#45 35.70 at org.gradle.api.internal.tasks.properties.InputParameterUtils.prepareInputParameterValue(InputParameterUtils.java:38)
#45 35.70 at org.gradle.api.internal.tasks.properties.InputParameterUtils.prepareInputParameterValue(InputParameterUtils.java:30)
#45 35.70 ... 69 more
#45 35.70 Caused by: org.gradle.api.InvalidUserDataException: Main class name has not been configured and it could not be resolved from classpath
#45 35.70 at org.springframework.boot.gradle.plugin.ResolveMainClassName$ClassNameReader.transform(ResolveMainClassName.java:173)
#45 35.70 at org.springframework.boot.gradle.plugin.ResolveMainClassName$ClassNameReader.transform(ResolveMainClassName.java:162)
#45 35.70 at org.gradle.api.internal.provider.ValueSupplier$Present.transform(ValueSupplier.java:511)
#45 35.70 at org.gradle.api.internal.provider.TransformBackedProvider.mapValue(TransformBackedProvider.java:91)
#45 35.70 at org.gradle.api.internal.provider.TransformBackedProvider.calculateOwnValue(TransformBackedProvider.java:82)
#45 35.70 at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:115)
#45 35.70 at org.gradle.api.internal.provider.FlatMapProvider.calculateOwnValue(FlatMapProvider.java:53)
#45 35.70 at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:115)
#45 35.70 at org.gradle.api.internal.provider.TransformBackedProvider.calculateOwnValue(TransformBackedProvider.java:81)
#45 35.70 at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:115)
#45 35.70 at org.gradle.api.internal.provider.AbstractMinimalProvider.withFinalValue(AbstractMinimalProvider.java:168)
#45 35.70 at org.gradle.api.internal.provider.DefaultProperty.finalValue(DefaultProperty.java:157)
#45 35.70 at org.gradle.api.internal.provider.DefaultProperty.finalValue(DefaultProperty.java:33)
#45 35.70 at org.gradle.api.internal.provider.AbstractProperty.finalizeNow(AbstractProperty.java:281)
This is Dockerfile running build at module directory
# Base image
FROM openjdk:17-jdk-slim
# Set working directory
WORKDIR /app
# Copy Gradle files from the root context to the service context
COPY gradlew /app/
COPY gradle /app/gradle/
COPY settings.gradle /app/
COPY build.gradle /app/
COPY module-common/build.gradle /app/module-common/
COPY module-adjust/build.gradle /app/module-adjust/
COPY module-common/src /app/module-common/src
COPY module-adjust/src /app/module-adjust/src
# Ensure gradlew is executable
RUN chmod +x gradlew
# Build the application
RUN ./gradlew build -x test --stacktrace
# Copy the built JAR file
COPY build/libs/adjustment-*.jar app.jar
# Expose port
EXPOSE 8080
# Run the application
CMD ["java", "-jar", "app.jar"]
And this is build.gradle code of module
dependencies {
implementation project(':module-common')
}
springBoot{
mainClass.set('com.sparta.AdjustApplication') // main class location
}
this is build.gradle code of root
plugins {
id 'application'
id 'java'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.3'
}
bootJar {
enabled = false
}
jar {
enabled = true
}
repositories {
mavenCentral()
}
subprojects { // 모든 하위 모듈들에 이 설정을 적용합니다.
group 'com.example'
version '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
test {
useJUnitPlatform()
}
}
The error code indicates that the issue is with finding the main class. So, I first checked whether the main class is incorrectly specified.
I tested whether other errors occur when excluding bootJar, and it didn’t change anything. I tried:
tasks.named('bootJar') {
enabled = false
}
And
bootJar {
enabled = false
}
jar {
enabled = true
}
- I also tried to specify the main class for bootRun:
bootRun {
mainClass = 'com.sparta.AdjustApplication' // Please enter the actual path of the main class here
}
Despite numerous attempts, I haven’t found a solution. If anyone has had similar experiences and found a resolution, please let me know! Thanks!