├── .sdkmanrc ├── settings.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── java │ │ └── com │ │ │ └── sivalabs │ │ │ └── myapp │ │ │ ├── users │ │ │ ├── config │ │ │ │ ├── package-info.java │ │ │ │ └── WebMvcConfig.java │ │ │ ├── domain │ │ │ │ ├── package-info.java │ │ │ │ ├── UserRepository.java │ │ │ │ ├── UserService.java │ │ │ │ └── User.java │ │ │ └── web │ │ │ │ └── controller │ │ │ │ ├── package-info.java │ │ │ │ └── UserController.java │ │ │ └── Application.java │ └── resources │ │ ├── db │ │ └── migration │ │ │ └── V1__Init.sql │ │ └── application.properties └── test │ ├── java │ └── com │ │ └── sivalabs │ │ └── myapp │ │ ├── TestApplication.java │ │ ├── ApplicationTests.java │ │ ├── TestcontainersConfig.java │ │ └── users │ │ └── web │ │ └── controller │ │ └── UserControllerTest.java │ └── resources │ └── logback-test.xml ├── renovate.json ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── .gitignore ├── sonar-project.properties ├── deployment └── docker-compose │ └── docker-compose.yml ├── .github └── workflows │ ├── ci-gradle.yml │ └── ci-maven.yml ├── gradlew.bat ├── README.gradle.md ├── README.md ├── mvnw.cmd ├── gradlew ├── LICENSE ├── mvnw └── pom.xml /.sdkmanrc: -------------------------------------------------------------------------------- 1 | java=25-tem 2 | maven=3.9.11 3 | gradle=9.2.1 4 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "spring-boot-application-template" 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivaprasadreddy/spring-boot-application-template/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/config/package-info.java: -------------------------------------------------------------------------------- 1 | @NullMarked 2 | package com.sivalabs.myapp.users.config; 3 | 4 | import org.jspecify.annotations.NullMarked; 5 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/domain/package-info.java: -------------------------------------------------------------------------------- 1 | @NullMarked 2 | package com.sivalabs.myapp.users.domain; 3 | 4 | import org.jspecify.annotations.NullMarked; 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | "schedule:earlyMondays" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/web/controller/package-info.java: -------------------------------------------------------------------------------- 1 | @NullMarked 2 | package com.sivalabs.myapp.users.web.controller; 3 | 4 | import org.jspecify.annotations.NullMarked; 5 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip 4 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/domain/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.domain; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | interface UserRepository extends JpaRepository {} 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/test/java/com/sivalabs/myapp/TestApplication.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | 5 | public class TestApplication { 6 | 7 | public static void main(String[] args) { 8 | SpringApplication.from(Application::main) 9 | .with(TestcontainersConfig.class) 10 | .run(args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/sivalabs/myapp/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @SpringBootTest 8 | @Import(TestcontainersConfig.class) 9 | class ApplicationTests { 10 | 11 | @Test 12 | void contextLoads() {} 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/Application.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V1__Init.sql: -------------------------------------------------------------------------------- 1 | create sequence user_id_seq start with 1 increment by 10; 2 | 3 | create table users 4 | ( 5 | id bigint DEFAULT nextval('user_id_seq') not null, 6 | email varchar(255) not null, 7 | password varchar(255) not null, 8 | name varchar(255) not null, 9 | primary key (id), 10 | constraint user_email_unique unique (email) 11 | ); 12 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | class WebMvcConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addViewControllers(ViewControllerRegistry registry) { 12 | registry.addRedirectViewController("/", "/actuator/health"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/com/sivalabs/myapp/TestcontainersConfig.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp; 2 | 3 | import org.springframework.boot.test.context.TestConfiguration; 4 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 5 | import org.springframework.context.annotation.Bean; 6 | import org.testcontainers.postgresql.PostgreSQLContainer; 7 | 8 | @TestConfiguration(proxyBeanMethods = false) 9 | public class TestcontainersConfig { 10 | @Bean 11 | @ServiceConnection 12 | public PostgreSQLContainer postgreSQLContainer() { 13 | return new PostgreSQLContainer("postgres:18-alpine"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | target/ 9 | !.mvn/wrapper/maven-wrapper.jar 10 | !**/src/main/**/target/ 11 | !**/src/test/**/target/ 12 | 13 | ### STS ### 14 | .apt_generated 15 | .classpath 16 | .factorypath 17 | .project 18 | .settings 19 | .springBeans 20 | .sts4-cache 21 | 22 | ### IntelliJ IDEA ### 23 | .idea 24 | *.iws 25 | *.iml 26 | *.ipr 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | build/ 35 | !**/src/main/**/build/ 36 | !**/src/test/**/build/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=spring-boot-application-template 2 | logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}] 3 | 4 | ################ Actuator ##################### 5 | management.endpoints.web.exposure.include=* 6 | management.metrics.distribution.percentiles-histogram.http.server.requests=true 7 | management.metrics.tags.application=${spring.application.name} 8 | management.tracing.sampling.probability=1.0 9 | management.health.mail.enabled=false 10 | management.tracing.enabled=false 11 | 12 | ################ Database ##################### 13 | spring.jpa.open-in-view=false 14 | spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true 15 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/domain/UserService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.domain; 2 | 3 | import java.util.List; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | @Service 8 | public class UserService { 9 | private final UserRepository userRepository; 10 | 11 | UserService(UserRepository userRepository) { 12 | this.userRepository = userRepository; 13 | } 14 | 15 | @Transactional(readOnly = true) 16 | public List getAllUsers() { 17 | return userRepository.findAll(); 18 | } 19 | 20 | @Transactional 21 | public User createUser(User user) { 22 | return userRepository.save(user); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.sourceEncoding=UTF-8 2 | sonar.projectKey=sivaprasadreddy_spring-boot-application-template 3 | sonar.projectName=spring-boot-application-template 4 | sonar.organization=sivaprasadreddy-github 5 | sonar.host.url=https://sonarcloud.io 6 | 7 | sonar.sources=src/main/java 8 | sonar.tests=src/test/java 9 | sonar.exclusions=src/main/java/**/config/*.*,src/main/java/**/entities/*.*,src/main/java/**/models/*.*,src/main/java/**/exceptions/*.*,src/main/java/**/utils/*.*,src/main/java/**/*Application.* 10 | sonar.test.inclusions=**/*Test.java,**/*IntegrationTest.java,**/*IT.java 11 | sonar.java.codeCoveragePlugin=jacoco 12 | sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml 13 | sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest 14 | -------------------------------------------------------------------------------- /deployment/docker-compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: postgres:18-alpine 4 | environment: 5 | POSTGRES_USER: postgres 6 | POSTGRES_PASSWORD: postgres 7 | POSTGRES_DB: postgres 8 | ports: 9 | - "5432" 10 | healthcheck: 11 | test: [ "CMD-SHELL", "pg_isready -U postgres" ] 12 | interval: 10s 13 | timeout: 5s 14 | retries: 5 15 | 16 | spring-boot-application-template: 17 | image: sivaprasadreddy/spring-boot-application-template 18 | container_name: spring-boot-application-template 19 | ports: 20 | - "8080:8080" 21 | restart: unless-stopped 22 | environment: 23 | SPRING_PROFILES_ACTIVE: docker 24 | SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.postgresql.Driver 25 | SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/postgres 26 | SPRING_DATASOURCE_USERNAME: postgres 27 | SPRING_DATASOURCE_PASSWORD: postgres 28 | deploy: 29 | resources: 30 | limits: 31 | memory: 1024m 32 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/web/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.web.controller; 2 | 3 | import com.sivalabs.myapp.users.domain.User; 4 | import com.sivalabs.myapp.users.domain.UserService; 5 | import jakarta.validation.Valid; 6 | import java.util.List; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequestMapping("/api/users") 17 | class UserController { 18 | private final UserService userService; 19 | 20 | UserController(UserService userService) { 21 | this.userService = userService; 22 | } 23 | 24 | @GetMapping 25 | List getUsers() { 26 | return userService.getAllUsers(); 27 | } 28 | 29 | @PostMapping 30 | ResponseEntity save(@Valid @RequestBody User user) { 31 | User savedUser = userService.createUser(user); 32 | return ResponseEntity.status(HttpStatus.CREATED).body(savedUser); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci-gradle.yml: -------------------------------------------------------------------------------- 1 | name: Gradle Build 2 | 3 | env: 4 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 5 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 6 | DOCKER_IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/spring-boot-application-template 7 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | pull_request: 14 | branches: [ "main" ] 15 | 16 | jobs: 17 | build: 18 | name: Build 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v6 23 | 24 | - name: Setup Java 25 | uses: actions/setup-java@v5 26 | with: 27 | java-version: 25 28 | distribution: 'temurin' 29 | cache: 'gradle' 30 | 31 | - name: Test 32 | run: ./gradlew build 33 | 34 | # - if: ${{ github.ref == 'refs/heads/main' }} 35 | # name: Sonar Scan 36 | # run: ./gradlew sonarqube 37 | # env: 38 | # SONAR_TOKEN: ${{ env.SONAR_TOKEN }} 39 | 40 | - if: ${{ github.ref == 'refs/heads/main' }} 41 | name: Build and Publish Docker Image 42 | run: | 43 | ./gradlew bootBuildImage --imageName=${{ env.DOCKER_IMAGE_NAME }} 44 | docker login -u ${{ env.DOCKER_USERNAME }} -p ${{ env.DOCKER_PASSWORD }} 45 | docker push ${{ env.DOCKER_IMAGE_NAME }} 46 | -------------------------------------------------------------------------------- /.github/workflows/ci-maven.yml: -------------------------------------------------------------------------------- 1 | name: Maven Build 2 | 3 | env: 4 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 5 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 6 | DOCKER_IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/spring-boot-application-template 7 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 8 | 9 | on: 10 | push: 11 | branches: 12 | - 'main' 13 | pull_request: 14 | branches: [ "main" ] 15 | 16 | jobs: 17 | build: 18 | name: Build 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v6 23 | 24 | - name: Setup Java 25 | uses: actions/setup-java@v5 26 | with: 27 | java-version: 25 28 | distribution: 'temurin' 29 | cache: 'maven' 30 | 31 | - name: Test 32 | run: ./mvnw verify 33 | 34 | # - if: ${{ github.ref == 'refs/heads/main' }} 35 | # name: Sonar Scan 36 | # run: ./mvnw initialize sonar:sonar -Dsonar.token=${{ env.SONAR_TOKEN }} 37 | 38 | - if: ${{ github.ref == 'refs/heads/main' }} 39 | name: Build and Publish Docker Image 40 | run: | 41 | ./mvnw spring-boot:build-image -DskipTests -Dspring-boot.build-image.imageName=${{ env.DOCKER_IMAGE_NAME }} 42 | docker login -u ${{ env.DOCKER_USERNAME }} -p ${{ env.DOCKER_PASSWORD }} 43 | docker push ${{ env.DOCKER_IMAGE_NAME }} 44 | -------------------------------------------------------------------------------- /src/main/java/com/sivalabs/myapp/users/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.domain; 2 | 3 | import jakarta.persistence.Column; 4 | import jakarta.persistence.Entity; 5 | import jakarta.persistence.GeneratedValue; 6 | import jakarta.persistence.GenerationType; 7 | import jakarta.persistence.Id; 8 | import jakarta.persistence.SequenceGenerator; 9 | import jakarta.persistence.Table; 10 | import jakarta.validation.constraints.NotEmpty; 11 | import org.jspecify.annotations.NullUnmarked; 12 | 13 | @Entity 14 | @Table(name = "users") 15 | @NullUnmarked 16 | public class User { 17 | 18 | @Id 19 | @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_generator") 20 | @SequenceGenerator(name = "user_id_generator", sequenceName = "user_id_seq", allocationSize = 10) 21 | private Long id; 22 | 23 | @Column(nullable = false, unique = true) 24 | @NotEmpty(message = "Email is required") private String email; 25 | 26 | @Column(nullable = false) 27 | @NotEmpty(message = "Password is required") private String password; 28 | 29 | @Column(nullable = false) 30 | @NotEmpty(message = "Name is required") private String name; 31 | 32 | public User() {} 33 | 34 | public Long getId() { 35 | return id; 36 | } 37 | 38 | public void setId(Long id) { 39 | this.id = id; 40 | } 41 | 42 | public String getEmail() { 43 | return email; 44 | } 45 | 46 | public void setEmail(String email) { 47 | this.email = email; 48 | } 49 | 50 | public String getPassword() { 51 | return password; 52 | } 53 | 54 | public void setPassword(String password) { 55 | this.password = password; 56 | } 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | public void setName(String name) { 63 | this.name = name; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/com/sivalabs/myapp/users/web/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.myapp.users.web.controller; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; 5 | 6 | import com.sivalabs.myapp.TestcontainersConfig; 7 | import com.sivalabs.myapp.users.domain.User; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; 12 | import org.springframework.context.annotation.Import; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.test.web.servlet.assertj.MockMvcTester; 16 | import org.springframework.test.web.servlet.assertj.MvcTestResult; 17 | 18 | @SpringBootTest(webEnvironment = RANDOM_PORT) 19 | @Import(TestcontainersConfig.class) 20 | @AutoConfigureMockMvc 21 | class UserControllerTest { 22 | 23 | @Autowired 24 | private MockMvcTester mockMvcTester; 25 | 26 | @Test 27 | void shouldFetchBookmarksByPageNumber() throws Exception { 28 | MvcTestResult testResult = mockMvcTester.get().uri("/api/users").exchange(); 29 | assertThat(testResult).hasStatusOk(); 30 | } 31 | 32 | @Test 33 | void shouldCreateBookmarkSuccessfully() throws Exception { 34 | MvcTestResult testResult = mockMvcTester 35 | .post() 36 | .uri("/api/users") 37 | .contentType(MediaType.APPLICATION_JSON) 38 | .content(""" 39 | { 40 | "email": "siva@gmail.com", 41 | "password": "secret", 42 | "name": "Siva" 43 | } 44 | """) 45 | .exchange(); 46 | 47 | assertThat(testResult) 48 | .hasStatus(HttpStatus.CREATED) 49 | .bodyJson() 50 | .convertTo(User.class) 51 | .satisfies(user -> { 52 | assertThat(user.getId()).isNotNull(); 53 | assertThat(user.getEmail()).isEqualTo("siva@gmail.com"); 54 | assertThat(user.getName()).isEqualTo("Siva"); 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /README.gradle.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Application Template 2 | This repository is a template for creating a Spring Boot application with commonly used features pre-configured. 3 | 4 | [![Maven Build](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-maven.yml/badge.svg)](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-maven.yml) 5 | [![Gradle Build](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-gradle.yml/badge.svg)](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-gradle.yml) 6 | 7 | ## Features 8 | * Spring Boot 9 | * Spring Data JPA 10 | * PostgreSQL 11 | * Flyway for DB migration 12 | * Springdoc Open API 13 | * JaCoCo code coverage check 14 | * SonarQube code quality check 15 | * JUnit 16 | * Testcontainers for testing & Local Dev 17 | * GitHub Actions 18 | * DockerCompose Deployment 19 | 20 | ## Prerequisites 21 | * Install Java using [SDKMAN](https://sdkman.io/) 22 | 23 | ```shell 24 | $ curl -s "https://get.sdkman.io" | bash 25 | $ source "$HOME/.sdkman/bin/sdkman-init.sh" 26 | $ sdk version 27 | $ sdk env install 28 | ``` 29 | * Install Docker : https://docs.docker.com/get-docker/ 30 | 31 | ## Getting Started 32 | 33 | ```shell 34 | $ git clone https://github.com/sivaprasadreddy/spring-boot-application-template.git 35 | $ cd spring-boot-application-template 36 | $ ./gradlew build 37 | ``` 38 | 39 | To run the application from IDE, run `TestApplication.java` under `src/test/java`. 40 | 41 | ## How to? 42 | This section describes how to perform various tasks. 43 | 44 | ### Code Formatting 45 | The [Spotless for Gradle](https://github.com/diffplug/spotless/tree/main/plugin-gradle) combined with 46 | [palantir-java-format](https://github.com/palantir/palantir-java-format) is used to format source code 47 | and is configured to automatically check code formatting while building the application. 48 | 49 | ```shell 50 | $ ./gradlew spotlessApply <- to format source code 51 | $ ./gradlew spotlessCheck <- to verify source code formatting 52 | ``` 53 | 54 | ### JaCoCo Code Coverage 55 | The [The JaCoCo Plugin](https://docs.gradle.org/current/userguide/jacoco_plugin.html) is used to verify the test code coverage. 56 | If the expected code coverage is not met (default is set to 80%) then the build will fail. 57 | 58 | ```shell 59 | $ ./gradlew jacocoTestCoverageVerification 60 | ``` 61 | 62 | ### SonarQube Quality Check 63 | The [Gradle SonarQube Plugin](https://plugins.gradle.org/plugin/org.sonarqube) is configured and 64 | is configured to run on [SonarCloud](https://sonarcloud.io/). 65 | 66 | You can configure the sonar properties in `sonar-project.properties` and run the sonar scan as follows: 67 | 68 | ```shell 69 | $ ./gradlew sonarqube -Dsonar.login=$SONAR_TOKEN 70 | ``` 71 | 72 | ### Create Docker Image 73 | The [spring-boot-gradle-plugin](https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/) provides 74 | the capability to create an [OCI image](https://github.com/opencontainers/image-spec) from a jar or war file using [Cloud Native Buildpacks](https://buildpacks.io/) (CNB). 75 | 76 | ```shell 77 | $ ./gradlew bootBuildImage --imageName=$DOCKER_USERNAME/$DOCKER_IMAGE_NAME 78 | ``` 79 | 80 | ### Run application using docker-compose 81 | Once the application docker image is created, you can run the application using docker-compose as follows: 82 | 83 | ```shell 84 | $ cd deployment/docker-compose 85 | $ docker compose up -d 86 | ``` 87 | 88 | Now the application should be accessible at http://localhost:8080/ 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Application Template 2 | This repository is a template for creating a Spring Boot application with commonly used features pre-configured. 3 | 4 | [![Maven Build](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-maven.yml/badge.svg)](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-maven.yml) 5 | [![Gradle Build](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-gradle.yml/badge.svg)](https://github.com/sivaprasadreddy/spring-boot-application-template/actions/workflows/ci-gradle.yml) 6 | 7 | ## Features 8 | * Spring Boot 9 | * Spring Data JPA 10 | * PostgreSQL 11 | * Flyway for DB migration 12 | * Springdoc Open API 13 | * JaCoCo code coverage check 14 | * SonarQube code quality check 15 | * JUnit 16 | * Testcontainers for testing & Local Dev 17 | * GitHub Actions 18 | * DockerCompose Deployment 19 | 20 | ## Prerequisites 21 | * Install Java using [SDKMAN](https://sdkman.io/) 22 | 23 | ```shell 24 | $ curl -s "https://get.sdkman.io" | bash 25 | $ source "$HOME/.sdkman/bin/sdkman-init.sh" 26 | $ sdk version 27 | $ sdk env install 28 | ``` 29 | * Install Docker : https://docs.docker.com/get-docker/ 30 | 31 | ## Getting Started 32 | 33 | ```shell 34 | $ git clone https://github.com/sivaprasadreddy/spring-boot-application-template.git 35 | $ cd spring-boot-application-template 36 | $ ./mvnw verify 37 | ``` 38 | 39 | To run the application from IDE, run `TestApplication.java` under `src/test/java`. 40 | 41 | ## How to? 42 | This section describes how to perform various tasks. 43 | 44 | ### Code Formatting 45 | The [spotless-maven-plugin](https://github.com/diffplug/spotless/tree/main/plugin-maven) combined with 46 | [palantir-java-format](https://github.com/palantir/palantir-java-format) is used to format source code 47 | and is configured to automatically check code formatting while building the application. 48 | 49 | ```shell 50 | $ ./mvnw spotless:apply <- to format source code 51 | $ ./mvnw spotless:check <- to verify source code formatting 52 | ``` 53 | 54 | ### JaCoCo Code Coverage 55 | The [jacoco-maven-plugin](https://www.eclemma.org/jacoco/trunk/doc/maven.html) is used to verify the test code coverage. 56 | If the expected code coverage is not met (default is set to 80%) then the build will fail. 57 | 58 | ```shell 59 | $ ./mvnw verify 60 | ``` 61 | 62 | ### SonarQube Quality Check 63 | The [sonar-maven-plugin](https://docs.sonarqube.org/latest/analyzing-source-code/scanners/sonarscanner-for-maven/) is configured and 64 | is configured to run on [SonarCloud](https://sonarcloud.io/). 65 | 66 | You can configure the sonar properties in `sonar-project.properties` and run the sonar scan as follows: 67 | 68 | ```shell 69 | $ ./mvnw initialize sonar:sonar -Dsonar.login=$SONAR_TOKEN 70 | ``` 71 | 72 | ### Create Docker Image 73 | The [spring-boot-maven-plugin](https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/) provides 74 | the capability to create an [OCI image](https://github.com/opencontainers/image-spec) from a jar or war file using [Cloud Native Buildpacks](https://buildpacks.io/) (CNB). 75 | 76 | ```shell 77 | $ ./mvnw spring-boot:build-image -DskipTests -DdockerImageName=$DOCKER_USERNAME/$DOCKER_IMAGE_NAME 78 | ``` 79 | 80 | ### Run application using docker-compose 81 | Once the application docker image is created, you can run the application using docker-compose as follows: 82 | 83 | ```shell 84 | $ cd deployment/docker-compose 85 | $ docker compose up -d 86 | ``` 87 | 88 | Now the application should be accessible at http://localhost:8080/ 89 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.4 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | 82 | $MAVEN_M2_PATH = "$HOME/.m2" 83 | if ($env:MAVEN_USER_HOME) { 84 | $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" 85 | } 86 | 87 | if (-not (Test-Path -Path $MAVEN_M2_PATH)) { 88 | New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null 89 | } 90 | 91 | $MAVEN_WRAPPER_DISTS = $null 92 | if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { 93 | $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" 94 | } else { 95 | $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" 96 | } 97 | 98 | $MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" 99 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 100 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 101 | 102 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 103 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 104 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 105 | exit $? 106 | } 107 | 108 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 109 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 110 | } 111 | 112 | # prepare tmp dir 113 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 114 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 115 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 116 | trap { 117 | if ($TMP_DOWNLOAD_DIR.Exists) { 118 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 119 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 120 | } 121 | } 122 | 123 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 124 | 125 | # Download and Install Apache Maven 126 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 127 | Write-Verbose "Downloading from: $distributionUrl" 128 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 129 | 130 | $webclient = New-Object System.Net.WebClient 131 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 132 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 133 | } 134 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 135 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 136 | 137 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 138 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 139 | if ($distributionSha256Sum) { 140 | if ($USE_MVND) { 141 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 142 | } 143 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 144 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 145 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 146 | } 147 | } 148 | 149 | # unzip and move 150 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 151 | 152 | # Find the actual extracted directory name (handles snapshots where filename != directory name) 153 | $actualDistributionDir = "" 154 | 155 | # First try the expected directory name (for regular distributions) 156 | $expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" 157 | $expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" 158 | if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { 159 | $actualDistributionDir = $distributionUrlNameMain 160 | } 161 | 162 | # If not found, search for any directory with the Maven executable (for snapshots) 163 | if (!$actualDistributionDir) { 164 | Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { 165 | $testPath = Join-Path $_.FullName "bin/$MVN_CMD" 166 | if (Test-Path -Path $testPath -PathType Leaf) { 167 | $actualDistributionDir = $_.Name 168 | } 169 | } 170 | } 171 | 172 | if (!$actualDistributionDir) { 173 | Write-Error "Could not find Maven distribution directory in extracted archive" 174 | } 175 | 176 | Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" 177 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null 178 | try { 179 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 180 | } catch { 181 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 182 | Write-Error "fail to move MAVEN_HOME" 183 | } 184 | } finally { 185 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 186 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 187 | } 188 | 189 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 190 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | 118 | 119 | # Determine the Java command to use to start the JVM. 120 | if [ -n "$JAVA_HOME" ] ; then 121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 122 | # IBM's JDK on AIX uses strange locations for the executables 123 | JAVACMD=$JAVA_HOME/jre/sh/java 124 | else 125 | JAVACMD=$JAVA_HOME/bin/java 126 | fi 127 | if [ ! -x "$JAVACMD" ] ; then 128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 129 | 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | if ! command -v java >/dev/null 2>&1 136 | then 137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 138 | 139 | Please set the JAVA_HOME variable in your environment to match the 140 | location of your Java installation." 141 | fi 142 | fi 143 | 144 | # Increase the maximum file descriptors if we can. 145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 146 | case $MAX_FD in #( 147 | max*) 148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 149 | # shellcheck disable=SC2039,SC3045 150 | MAX_FD=$( ulimit -H -n ) || 151 | warn "Could not query maximum file descriptor limit" 152 | esac 153 | case $MAX_FD in #( 154 | '' | soft) :;; #( 155 | *) 156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 157 | # shellcheck disable=SC2039,SC3045 158 | ulimit -n "$MAX_FD" || 159 | warn "Could not set maximum file descriptor limit to $MAX_FD" 160 | esac 161 | fi 162 | 163 | # Collect all arguments for the java command, stacking in reverse order: 164 | # * args from the command line 165 | # * the main class name 166 | # * -classpath 167 | # * -D...appname settings 168 | # * --module-path (only if needed) 169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 170 | 171 | # For Cygwin or MSYS, switch paths to Windows format before running java 172 | if "$cygwin" || "$msys" ; then 173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.4 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | scriptDir="$(dirname "$0")" 109 | scriptName="$(basename "$0")" 110 | 111 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 112 | while IFS="=" read -r key value; do 113 | case "${key-}" in 114 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 115 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 116 | esac 117 | done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" 118 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 119 | 120 | case "${distributionUrl##*/}" in 121 | maven-mvnd-*bin.*) 122 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 123 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 124 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 125 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 126 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 127 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 128 | *) 129 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 130 | distributionPlatform=linux-amd64 131 | ;; 132 | esac 133 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 134 | ;; 135 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 136 | *) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 137 | esac 138 | 139 | # apply MVNW_REPOURL and calculate MAVEN_HOME 140 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 141 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 142 | distributionUrlName="${distributionUrl##*/}" 143 | distributionUrlNameMain="${distributionUrlName%.*}" 144 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 145 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 146 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 147 | 148 | exec_maven() { 149 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 150 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 151 | } 152 | 153 | if [ -d "$MAVEN_HOME" ]; then 154 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 155 | exec_maven "$@" 156 | fi 157 | 158 | case "${distributionUrl-}" in 159 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 160 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 161 | esac 162 | 163 | # prepare tmp dir 164 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 165 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 166 | trap clean HUP INT TERM EXIT 167 | else 168 | die "cannot create temp dir" 169 | fi 170 | 171 | mkdir -p -- "${MAVEN_HOME%/*}" 172 | 173 | # Download and Install Apache Maven 174 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 175 | verbose "Downloading from: $distributionUrl" 176 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 177 | 178 | # select .zip or .tar.gz 179 | if ! command -v unzip >/dev/null; then 180 | distributionUrl="${distributionUrl%.zip}.tar.gz" 181 | distributionUrlName="${distributionUrl##*/}" 182 | fi 183 | 184 | # verbose opt 185 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 186 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 187 | 188 | # normalize http auth 189 | case "${MVNW_PASSWORD:+has-password}" in 190 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 191 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 192 | esac 193 | 194 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 195 | verbose "Found wget ... using wget" 196 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 197 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 198 | verbose "Found curl ... using curl" 199 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 200 | elif set_java_home; then 201 | verbose "Falling back to use Java to download" 202 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 203 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 204 | cat >"$javaSource" <<-END 205 | public class Downloader extends java.net.Authenticator 206 | { 207 | protected java.net.PasswordAuthentication getPasswordAuthentication() 208 | { 209 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 210 | } 211 | public static void main( String[] args ) throws Exception 212 | { 213 | setDefault( new Downloader() ); 214 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 215 | } 216 | } 217 | END 218 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 219 | verbose " - Compiling Downloader.java ..." 220 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 221 | verbose " - Running Downloader.java ..." 222 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 223 | fi 224 | 225 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 226 | if [ -n "${distributionSha256Sum-}" ]; then 227 | distributionSha256Result=false 228 | if [ "$MVN_CMD" = mvnd.sh ]; then 229 | echo "Checksum validation is not supported for maven-mvnd." >&2 230 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 231 | exit 1 232 | elif command -v sha256sum >/dev/null; then 233 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then 234 | distributionSha256Result=true 235 | fi 236 | elif command -v shasum >/dev/null; then 237 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 238 | distributionSha256Result=true 239 | fi 240 | else 241 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 242 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 243 | exit 1 244 | fi 245 | if [ $distributionSha256Result = false ]; then 246 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 247 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 248 | exit 1 249 | fi 250 | fi 251 | 252 | # unzip and move 253 | if command -v unzip >/dev/null; then 254 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 255 | else 256 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 257 | fi 258 | 259 | # Find the actual extracted directory name (handles snapshots where filename != directory name) 260 | actualDistributionDir="" 261 | 262 | # First try the expected directory name (for regular distributions) 263 | if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then 264 | if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then 265 | actualDistributionDir="$distributionUrlNameMain" 266 | fi 267 | fi 268 | 269 | # If not found, search for any directory with the Maven executable (for snapshots) 270 | if [ -z "$actualDistributionDir" ]; then 271 | # enable globbing to iterate over items 272 | set +f 273 | for dir in "$TMP_DOWNLOAD_DIR"/*; do 274 | if [ -d "$dir" ]; then 275 | if [ -f "$dir/bin/$MVN_CMD" ]; then 276 | actualDistributionDir="$(basename "$dir")" 277 | break 278 | fi 279 | fi 280 | done 281 | set -f 282 | fi 283 | 284 | if [ -z "$actualDistributionDir" ]; then 285 | verbose "Contents of $TMP_DOWNLOAD_DIR:" 286 | verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" 287 | die "Could not find Maven distribution directory in extracted archive" 288 | fi 289 | 290 | verbose "Found extracted Maven distribution directory: $actualDistributionDir" 291 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" 292 | mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 293 | 294 | clean || : 295 | exec_maven "$@" 296 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.springframework.boot 6 | spring-boot-starter-parent 7 | 4.0.0 8 | 9 | 10 | 11 | com.sivalabs 12 | spring-boot-application-template 13 | 0.0.1-SNAPSHOT 14 | spring-boot-application-template 15 | Spring Boot Application Template 16 | 17 | 25 18 | 3.0.0 19 | 3.1.0 20 | 2.83.0 21 | 12.1.9 22 | 1.2.1 23 | 5.3.0.6276 24 | 0.8.14 25 | 80% 26 | ${project.build.directory}/test-results 27 | ${project.build.directory}/jacoco/test 28 | ${jacoco.utReportFolder}/test.exec 29 | ${project.build.directory}/jacoco/integrationTest 30 | ${jacoco.itReportFolder}/integrationTest.exec 31 | ${project.testresult.directory}/test 32 | ${project.testresult.directory}/integrationTest 33 | sivaprasadreddy/${project.artifactId} 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-webmvc 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-validation 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter-data-jpa 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-flyway 51 | 52 | 53 | org.flywaydb 54 | flyway-database-postgresql 55 | runtime 56 | 57 | 58 | org.postgresql 59 | postgresql 60 | runtime 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-actuator 65 | 66 | 67 | io.micrometer 68 | micrometer-registry-prometheus 69 | 70 | 71 | org.springdoc 72 | springdoc-openapi-starter-webmvc-ui 73 | ${springdoc-openapi.version} 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-devtools 79 | runtime 80 | true 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-configuration-processor 85 | true 86 | 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-starter-webmvc-test 91 | test 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-starter-data-jpa-test 96 | test 97 | 98 | 99 | org.springframework.boot 100 | spring-boot-starter-flyway-test 101 | test 102 | 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-testcontainers 107 | test 108 | 109 | 110 | org.testcontainers 111 | testcontainers-junit-jupiter 112 | test 113 | 114 | 115 | org.testcontainers 116 | testcontainers-postgresql 117 | test 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-compiler-plugin 126 | 127 | ${java.version} 128 | ${java.version} 129 | ${java.version} 130 | UTF-8 131 | true 132 | 133 | -XDcompilePolicy=simple 134 | --should-stop=ifError=FLOW 135 | -Xplugin:ErrorProne -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked -XepExcludedPaths:.*/generated-sources/.* -XepOpt:NullAway:TreatGeneratedAsUnannotated=true 136 | -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED 137 | -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED 138 | -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED 139 | -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED 140 | -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED 141 | -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED 142 | -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED 143 | -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 144 | -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED 145 | -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED 146 | 147 | 148 | 149 | org.springframework.boot 150 | spring-boot-configuration-processor 151 | 152 | 153 | 154 | com.google.errorprone 155 | error_prone_core 156 | 2.45.0 157 | 158 | 159 | com.uber.nullaway 160 | nullaway 161 | 0.12.12 162 | 163 | 164 | 165 | 166 | 167 | org.springframework.boot 168 | spring-boot-maven-plugin 169 | 170 | 171 | ${dockerImageName} 172 | 173 | 174 | 175 | 176 | 177 | build-info 178 | 179 | 180 | 181 | 182 | 183 | io.github.git-commit-id 184 | git-commit-id-maven-plugin 185 | 186 | false 187 | false 188 | true 189 | 190 | ^git.branch$ 191 | ^git.commit.id.abbrev$ 192 | ^git.commit.user.name$ 193 | ^git.commit.message.full$ 194 | 195 | 196 | 197 | 198 | 199 | revision 200 | 201 | 202 | 203 | 204 | 205 | org.apache.maven.plugins 206 | maven-surefire-plugin 207 | 208 | alphabetical 209 | ${junit.utReportFolder} 210 | 211 | **/*IT* 212 | **/*IntTest* 213 | **/*IntegrationTest* 214 | 215 | 216 | 217 | 218 | org.apache.maven.plugins 219 | maven-failsafe-plugin 220 | 221 | ${project.build.outputDirectory} 222 | alphabetical 223 | ${junit.itReportFolder} 224 | 225 | **/*IT* 226 | **/*IntTest* 227 | **/*IntegrationTest* 228 | 229 | 230 | 231 | 232 | integration-test 233 | 234 | integration-test 235 | 236 | 237 | 238 | verify 239 | 240 | verify 241 | 242 | 243 | 244 | 245 | 246 | org.codehaus.mojo 247 | properties-maven-plugin 248 | ${properties-maven-plugin.version} 249 | 250 | 251 | 252 | read-project-properties 253 | 254 | initialize 255 | 256 | 257 | sonar-project.properties 258 | 259 | 260 | 261 | 262 | 263 | 264 | com.diffplug.spotless 265 | spotless-maven-plugin 266 | ${spotless.version} 267 | 268 | 269 | 270 | ${palantir-java-format.version} 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | pom.xml 279 | 280 | 281 | false 282 | true 283 | 284 | 285 | 286 | 287 | 288 | 289 | check 290 | 291 | 292 | 293 | 294 | 295 | org.jacoco 296 | jacoco-maven-plugin 297 | ${jacoco-maven-plugin.version} 298 | 299 | 300 | pre-unit-tests 301 | 302 | prepare-agent 303 | 304 | 305 | 306 | ${jacoco.utReportFile} 307 | 308 | 309 | 310 | 311 | post-unit-test 312 | 313 | report 314 | 315 | test 316 | 317 | ${jacoco.utReportFile} 318 | ${jacoco.utReportFolder} 319 | 320 | 321 | 322 | default-check 323 | 324 | check 325 | 326 | 327 | ${jacoco.utReportFile} 328 | 329 | 330 | BUNDLE 331 | 332 | 333 | LINE 334 | COVEREDRATIO 335 | ${jacoco.minimum.coverage} 336 | 337 | 338 | 339 | 340 | 341 | **/*Application.* 342 | **/config/** 343 | **/models/* 344 | **/dtos/* 345 | **/exceptions/* 346 | **/*Constants* 347 | 348 | 349 | 350 | 351 | pre-integration-tests 352 | 353 | prepare-agent-integration 354 | 355 | 356 | 357 | ${jacoco.itReportFile} 358 | 359 | 360 | 361 | 362 | post-integration-tests 363 | 364 | report-integration 365 | 366 | post-integration-test 367 | 368 | ${jacoco.itReportFile} 369 | ${jacoco.itReportFolder} 370 | 371 | 372 | 373 | 374 | 375 | org.sonarsource.scanner.maven 376 | sonar-maven-plugin 377 | ${sonar-maven-plugin.version} 378 | 379 | 380 | 381 | sonar 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | --------------------------------------------------------------------------------