-Layered Architecture Template concept diagram -
- -Layered Architecture remains a popular pattern for enterprise applications thanks to its clear separation of concerns and ease of understanding. -Spring Boot's convention-over-configuration approach and built-in support for RESTful services, data access, and testing tools make it particularly well-suited for implementing this pattern. -Together, they provide a solid foundation that helps teams quickly build and maintain scalable applications without unnecessary complexity. - -Consequently, this repository provides a template implementation of a microservice following the Layered Architecture pattern, developed in Java with Spring Boot. It consists of: -* **API Layer** - * REST controllers handling HTTP requests -* **Service Layer** - * Business logic and service operations -* **Repository Layer** - * Data access and persistence interface -* **Supporting Components** - * Swagger for API documentation - * OpenAPI specifications - * Spring Boot Actuator for monitoring and management -* **Database Layer** - * In-memory H2 database for development and testing - -Please keep in mind this project serves as a basic template, providing core support for HTTP request handling and database interactions. -It's flexible by design, allowing you to add features or integrations as your requirements evolve. - -## When to Use Layered Architecture - -Layered Architecture organizes your application into distinct layers, each with a specific responsibility such as presentation, business logic, and data access. -This clear separation simplifies development, improves maintainability, and helps enforce separation of concerns across your codebase. - -This approach is particularly effective for projects with well-defined and stable requirements, where a straightforward division between layers can improve team collaboration and speed up delivery. -It works well for applications that primarily follow a request-response model, such as typical REST APIs, and where concerns like UI, service logic, and database access can be cleanly separated. -For smaller or less complex projects, Layered Architecture offers a familiar and easy-to-understand structure that helps avoid unnecessary complexity. -It also suits teams that prefer conventional architectural patterns or need quick onboarding of new developers. - -However, for systems requiring high flexibility or integration with multiple external interfaces, more decoupled architectures like Hexagonal or Event-Driven might provide better adaptability. -In such cases, you might be interested in the [Hexagonal Architecture Template](https://kamilmazurek.pl/hexagonal-architecture-template). - -Layered Architecture remains a solid choice when the focus is on clear organization, testability, and incremental development. -Ultimately, use Layered Architecture when your project benefits from a clear hierarchical structure that promotes simplicity, maintainability, and team alignment, especially when the application is expected to evolve steadily within a defined scope. - -## Technology Stack - -Layered Architecture Template is built using Java and Spring Boot, which naturally support modular design and clear separation between layers. -It uses the in-memory H2 database for quick prototyping and testing, but thanks to Spring Data, switching to another database later is simple and straightforward. - -OpenAPI is used to clearly define the RESTful APIs exposed by the API layer, helping maintain a clean separation between layers and simplifying client generation. -This aligns well with the layered architecture's goal of separating presentation from business logic. - -Testing is an integral part of the stack, with unit tests focusing on individual service and repository layers, and integration tests verifying end-to-end flow across layers. -Maven Surefire and Failsafe plugins ensure smooth test execution during builds. - -Here’s an overview of the technology stack: -- **Language & Framework** - - **Java 21**: Modern Java version powering the core application logic. - - **Spring Boot**: Simplifies building modular, RESTful Java applications. -- **API & Data** - - **OpenAPI**: Defines clear REST API specs and supports client generation. - - **ModelMapper**: Helps map data between layers smoothly. - - **H2 database**: Lightweight in-memory database for development and testing. -- **Testing** - - **JUnit**: Core framework for unit testing Java code. - - **REST Assured**: Integration testing for REST APIs. - - **Mockito**: Mocks dependencies to isolate components during tests. - - **Allure Report**: Generates detailed and user-friendly test reports. -- **Build & Deployment** - - **Apache Maven**: Manages builds and dependencies efficiently. - - **Docker**: Packages the app into containers for consistent deployment. - -This stack was picked to support the layered design, focusing on clear separation, ease of testing, and flexibility to grow with the application's needs. -It provides a solid foundation for building maintainable, modular, and production-ready microservices. - -## How It Works - -This implementation follows Layered Architecture principles by organizing the application into distinct layers with clear responsibilities. -The key layers here are the presentation layer (controller), service layer (business logic), and data access layer (repository). -To illustrate how these layers interact, let's walk through a typical Read use case, starting with a `GET` request handled by the controller. - -The `ItemsController` serves as the entry point for incoming HTTP requests. It receives the GET request, delegates processing to the service layer, and returns the appropriate HTTP response: - -```java -@RestController -@AllArgsConstructor -public class ItemsController implements ItemsApi { - - private final ItemsService service; - - @Override - public ResponseEntity-Sample Swagger UI view. For more details about Swagger, visit -https://swagger.io -
- -The OpenAPI `/api-docs` endpoint provides a machine-readable JSON specification of the API. This standardized format allows easy integration with various development tools, documentation generators, and client code generators, helping to maintain clear contracts between layers and teams. Sharing this specification fosters better collaboration and ensures that the API remains consistent with the layered architecture principles guiding the project. - -These tools help reinforce the separation of concerns by clearly exposing the API endpoints managed by the presentation layer while hiding the complexities of the underlying service and data layers. -By maintaining this clear contract through Swagger and OpenAPI, you ensure that each layer can evolve independently without breaking the overall system architecture. - - -## Production-ready Features - -The application uses Spring Boot Actuator, a library that adds production-ready features to Spring Boot applications. It provides capabilities like monitoring and health checks, which are enabled through the included configuration. -These features allow you to observe the health and status of the application across its layers, from the presentation layer down to the data layer, helping ensure that each part of the layered architecture is functioning properly. - -Two important actuator endpoints configured in this template are: -* `/actuator` which lists all exposed actuator endpoints: http://localhost:8080/actuator/ -* `/actuator/health` which shows the current health status of the application: http://localhost:8080/actuator/health - -You can find the list of available actuator endpoints by accessing the `/actuator` endpoint in your running application. -This list can be customized by modifying the `management.endpoints.web.exposure.include` property in [application.yaml](src/main/resources/application.yaml). - -For example, to enable the `beans` endpoint, add it to the `management.endpoints.web.exposure.include` list like this: -```yaml -management: - endpoints: - web: - exposure: - include: health, beans -``` - -For more details, visit the Spring Boot Actuator documentation: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html - -You can verify the health status of the application by sending a request to the `/actuator/health` endpoint. -The response will include the current state of the application, such as: -```console -http://localhost:8080/actuator/health -``` -```json -{ - "status": "UP" -} -``` - -By default, the application provides only basic health status information for security reasons. -If more detailed information is needed, such as disk space usage or database connectivity, this can be enabled by updating the [application.yaml](src/main/resources/application.yaml) configuration file as shown below: -```yaml -management: - endpoint: - health: - show-details: "always" -``` - -Applying this change results in more detailed information at the `/actuator/health` endpoint. -It also enables additional endpoints like `/actuator/health/db`, which provide details about the database: -```console -http://localhost:8080/actuator/health/db -``` -```json -{ - "status": "UP", - "details": { - "database": "H2", - "validationQuery": "isValid()" - } -} -``` - -These features help you monitor and maintain the application effectively, providing valuable insights into its health and performance across all layers of the architecture. -Proper use of actuator endpoints can improve reliability and simplify troubleshooting in both development and production environments. - -**Important:** In production environments, actuator endpoints should be secured to prevent unauthorized access. It is recommended to restrict access using authentication and authorization mechanisms. Be cautious when enabling detailed health information or sensitive endpoints. - -## Testing Strategy - -This project includes a structured testing setup that combines unit tests and integration tests, helping ensure both individual components and their interactions behave as expected. -Test execution is handled using Maven’s Surefire and Failsafe plugins, which are configured out of the box. - -Testing is built around well-established tools and libraries: -* JUnit: Used for writing test cases and defining assertions. -* Mockito: Helps simulate dependencies in unit testing. -* REST Assured: Simplifies testing REST endpoints during integration testing. - -This layered approach to testing aligns with the project's architecture and encourages maintainable, focused tests at every level. - -There are two main categories of tests included in this project: -* Unit tests (`*Test.java`), which verify individual classes or methods in isolation. These run using the Maven Surefire Plugin. -* Integration tests (`*IntegrationTest.java`), used to verify how components interact. These tests run with the Maven Failsafe Plugin. - -Here is a simple example of a JUnit unit test, `ItemsControllerTest`, which tests the `ItemsController` behavior. -It uses Mockito to mock the behavior of `ItemsService`, then requests an item using the controller and validates the response: -```java -class ItemsControllerTest { - - @Test - void shouldGetItem() { - //given item - var item = new ItemDTO().id(1L).name("Item A"); - - //and service - var service = mock(ItemsService.class); - when(service.getItem(1L)).thenReturn(Optional.of(item)); - - //and controller - var controller = new ItemsController(service); - - //when item is requested - var response = controller.getItem(1L); - - //then response containing expected item is returned - assertEquals(item, response.getBody()); - - //and OK status is returned - assertEquals(OK, response.getStatusCode()); - - //and service was involved in retrieving the data - verify(service).getItem(1L); - } - - (...) - -} -``` -Following that, here's an example of an integration test, `ItemsControllerIntegrationTest`, which verifies how multiple components work together to handle requests and responses. -This is also a JUnit test, but this one runs with `@SpringBootTest`, so it starts an H2 database and Spring context, including controllers, database connections and more. It uses REST-assured to perform requests and validate responses, testing the actual behavior across multiple layers: -```java -class ItemsControllerIntegrationTest extends AbstractIntegrationTest { - - private final ObjectWriter objectWriter = new ObjectMapper().writer(); - - @Test - void shouldGetItem() throws JsonProcessingException { - when() - .get("/items/1") - .then() - .statusCode(200) - .assertThat() - .body(equalTo(objectWriter.writeValueAsString(new ItemDTO().id(1L).name("Item A")))); - } - - (...) - -} -``` - -Unit tests, which verify individual components in isolation, can be executed using the Maven Surefire Plugin by running the following command: -```console -mvnw clean test -``` - -Integration tests, which validate how multiple components work together within the application, can be run using the Maven Failsafe Plugin: -```console -mvnw clean integration-test -``` -Note that this command also executes unit tests as part of the build lifecycle. - -Both unit and integration tests are executed automatically during the standard Maven build process: -```console -mvnw clean install -``` -This helps ensure that the application behaves correctly across individual components as well as across layers, from the service logic to the data access, before packaging or deployment. - -In addition, the project is configured to work with Allure Report, which provides a visual representation of test execution results. -You can generate and open the report in your browser by running the following commands: -```console -mvnw clean integration-test -mvnw allure:serve -``` -The report provides a clear overview of test results, including which tests passed or failed, how long they took to run, and the overall coverage. -A sample view of the generated report is shown below: - - --Sample Allure Report. For more information, visit -https://allurereport.org/ -
- -This testing setup supports the layered architecture by ensuring that each level, from isolated service logic to fully integrated REST interactions, is thoroughly verified. -It helps maintain confidence that every layer of the application behaves reliably both on its own and in coordination with others. - -## Additional resources -* [Layered Architecture, Baeldung](https://www.baeldung.com/cs/layered-architecture) -* [Multitier architecture, Wikipedia](https://en.wikipedia.org/wiki/Multitier_architecture) -* [Repository Pattern with Layered Architecture, Medium](https://medium.com/@leadcoder/repository-pattern-with-layered-architecture-35f7b9182ebf) -* [Layered Architecture Template on LibHunt](https://www.libhunt.com/r/layered-architecture-template) - -## Author -This project was created by [Kamil Mazurek](https://kamilmazurek.pl), a Software Engineer based in Warsaw, Poland. -You can also find me on my [LinkedIn profile](https://www.linkedin.com/in/kamil-mazurek). Thanks for visiting 🙂 - -## Disclaimer - -THIS SOFTWARE AND ANY ACCOMPANYING DOCUMENTATION (INCLUDING, BUT NOT LIMITED TO, THE README.MD FILE) ARE PROVIDED -FOR EDUCATIONAL PURPOSES ONLY. - -THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE, -THE DOCUMENTATION, OR THE USE OR OTHER DEALINGS IN THE SOFTWARE OR DOCUMENTATION. - -Spring Boot is a trademark of Broadcom Inc. and/or its subsidiaries. -Oracle, Java, MySQL, and NetSuite are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..1fc64f1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,148 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.5.4' + id 'io.spring.dependency-management' version '1.1.7' + id 'org.openapi.generator' version '7.9.0' + id 'io.qameta.allure' version '2.12.0' +} + +group = 'template' +version = '1.0.0-SNAPSHOT' +description = 'Layered Architecture Template' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +// The repo contains two @SpringBootApplication classes (the leftover +// 'template.Application' and the real 'com.goi.integration.IntegrationServiceApplication'). +// Pin the main class so bootJar/bootRun are unambiguous. +springBoot { + mainClass = 'com.goi.integration.IntegrationServiceApplication' +} + +ext { + jacksonDatabindNullableVersion = '0.2.6' + springdocVersion = '2.8.6' + modelmapperVersion = '3.2.2' + allureVersion = '2.29.0' + guavaVersion = '33.4.8-jre' +} + +// Equivalent of the Maven 'default' / 'dev' profiles, which only set the +// 'activeProfile' property that is filtered into application.yaml. +// Override with: ./gradlew bootRun -PactiveProfile=dev +def activeProfile = project.findProperty('activeProfile') ?: 'default' + +repositories { + mavenCentral() +} + +dependencies { + // spring + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-webflux' + + // api + implementation "org.openapitools:jackson-databind-nullable:${jacksonDatabindNullableVersion}" + implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}" + + // persistence + runtimeOnly 'com.h2database:h2' + + // util + implementation "com.google.guava:guava:${guavaVersion}" + implementation "org.modelmapper:modelmapper:${modelmapperVersion}" + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.rest-assured:rest-assured' + testImplementation "io.qameta.allure:allure-junit5:${allureVersion}" + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' +} + +// --------------------------------------------------------------------------- +// OpenAPI code generation (replaces openapi-generator-maven-plugin) +// --------------------------------------------------------------------------- +def openApiOutputDir = layout.buildDirectory.dir('generated/openapi') + +openApiGenerate { + generatorName = 'spring' + inputSpec = layout.projectDirectory.file('src/main/resources/api.yaml').asFile.toString() + outputDir = openApiOutputDir.get().asFile.toString() + apiPackage = 'template.api' + modelPackage = 'template.api.model' + configOptions = [ + interfaceOnly : 'true', + useSpringBoot3: 'true', + useTags : 'true' + ] + // keep the generated output limited to interfaces + models + generateApiTests = false + generateApiDocumentation = false + generateModelTests = false + generateModelDocumentation = false +} + +sourceSets { + main { + java { + srcDir openApiOutputDir.map { it.dir('src/main/java') } + } + } +} + +tasks.named('compileJava') { + dependsOn tasks.named('openApiGenerate') +} + +// --------------------------------------------------------------------------- +// Resource filtering: replaces Maven's @activeProfile@ token in application.yaml +// --------------------------------------------------------------------------- +tasks.named('processResources') { + inputs.property('activeProfile', activeProfile) + filesMatching(['**/application*.yaml', '**/application*.yml', '**/application*.properties']) { + filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [activeProfile: activeProfile.toString()]) + } +} + +// --------------------------------------------------------------------------- +// Tests: 'test' runs unit tests, 'integrationTest' runs *IntegrationTest +// (replaces the Maven surefire/failsafe split) +// --------------------------------------------------------------------------- +tasks.named('test', Test) { + useJUnitPlatform() + exclude '**/*IntegrationTest.*' +} + +tasks.register('integrationTest', Test) { + description = 'Runs integration tests (*IntegrationTest).' + group = 'verification' + useJUnitPlatform() + include '**/*IntegrationTest.*' + testClassesDirs = sourceSets.test.output.classesDirs + classpath = sourceSets.test.runtimeClasspath + shouldRunAfter tasks.named('test') +} + +tasks.named('check') { + dependsOn tasks.named('integrationTest') +} + +// --------------------------------------------------------------------------- +// Allure reporting (replaces allure-maven) +// --------------------------------------------------------------------------- +allure { + version = allureVersion + adapter { + autoconfigure = true + aspectjWeaver = true + } +} diff --git a/dockerfile b/dockerfile index d649f33..bdf3067 100644 --- a/dockerfile +++ b/dockerfile @@ -1,5 +1,5 @@ FROM openjdk:21-jdk-slim RUN addgroup --system template-group && adduser --system --ingroup template-group template-user USER template-user:template-group -COPY target/layered-architecture-template*.jar layered-architecture-template.jar -ENTRYPOINT ["java","-jar","/layered-architecture-template.jar"] \ No newline at end of file +COPY build/libs/layered-architecture-template-*.jar layered-architecture-template.jar +ENTRYPOINT ["java","-jar","/layered-architecture-template.jar"] diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..31e9305 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,8 @@ +# Build settings +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 + +# Spring profile filtered into application.yaml (Maven 'default'/'dev' profile equivalent) +# Override on the command line, e.g. ./gradlew bootRun -PactiveProfile=dev +activeProfile=default diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..d64cd49 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mvnw b/mvnw deleted file mode 100644 index 19529dd..0000000 --- a/mvnw +++ /dev/null @@ -1,259 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.2 -# -# Optional ENV vars -# ----------------- -# JAVA_HOME - location of a JDK home dir, required when download maven via java source -# MVNW_REPOURL - repo url base for downloading maven distribution -# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output -# ---------------------------------------------------------------------------- - -set -euf -[ "${MVNW_VERBOSE-}" != debug ] || set -x - -# OS specific support. -native_path() { printf %s\\n "$1"; } -case "$(uname)" in -CYGWIN* | MINGW*) - [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" - native_path() { cygpath --path --windows "$1"; } - ;; -esac - -# set JAVACMD and JAVACCMD -set_java_home() { - # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched - if [ -n "${JAVA_HOME-}" ]; then - if [ -x "$JAVA_HOME/jre/sh/java" ]; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - JAVACCMD="$JAVA_HOME/jre/sh/javac" - else - JAVACMD="$JAVA_HOME/bin/java" - JAVACCMD="$JAVA_HOME/bin/javac" - - if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then - echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 - echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 - return 1 - fi - fi - else - JAVACMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v java - )" || : - JAVACCMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v javac - )" || : - - if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then - echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 - return 1 - fi - fi -} - -# hash string like Java String::hashCode -hash_string() { - str="${1:-}" h=0 - while [ -n "$str" ]; do - char="${str%"${str#?}"}" - h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) - str="${str#?}" - done - printf %x\\n $h -} - -verbose() { :; } -[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - -die() { - printf %s\\n "$1" >&2 - exit 1 -} - -trim() { - # MWRAPPER-139: - # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. - # Needed for removing poorly interpreted newline sequences when running in more - # exotic environments such as mingw bash on Windows. - printf "%s" "${1}" | tr -d '[:space:]' -} - -# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties -while IFS="=" read -r key value; do - case "${key-}" in - distributionUrl) distributionUrl=$(trim "${value-}") ;; - distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; - esac -done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" -[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" - -case "${distributionUrl##*/}" in -maven-mvnd-*bin.*) - MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ - case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in - *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; - :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; - :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; - :Linux*x86_64*) distributionPlatform=linux-amd64 ;; - *) - echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 - distributionPlatform=linux-amd64 - ;; - esac - distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" - ;; -maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; -esac - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-