diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 591cc09..2a7fcf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: matrix: # The MODULE environment variable is evaluated in build-all.sh to run a subset # of the builds. This way, multiple modules can be built in parallel. - module: [ "module1", "module2", "module3", "module4", "module5" ] + module: [ "module1", "module2", "module3", "module4", "module5", "module6" ] steps: diff --git a/aws/spring-cloud-caching-redis/Dockerfile b/aws/spring-cloud-caching-redis/Dockerfile new file mode 100644 index 0000000..f5b6f23 --- /dev/null +++ b/aws/spring-cloud-caching-redis/Dockerfile @@ -0,0 +1,5 @@ +FROM --platform=linux/amd64 adoptopenjdk/openjdk11:jre-11.0.10_9-alpine +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} service.jar +EXPOSE 8080 +ENTRYPOINT [ "sh", "-c", "java -jar -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 service.jar" ] diff --git a/aws/spring-cloud-caching-redis/README.md b/aws/spring-cloud-caching-redis/README.md new file mode 100644 index 0000000..f69b0d6 --- /dev/null +++ b/aws/spring-cloud-caching-redis/README.md @@ -0,0 +1,4 @@ +# Caching In Spring Boot Application with ElastiCache for Redis + +* This example showcases how you can configure spring cache and connect it with +AWS ElastiCache diff --git a/aws/spring-cloud-caching-redis/build.gradle b/aws/spring-cloud-caching-redis/build.gradle new file mode 100644 index 0000000..b46d9a0 --- /dev/null +++ b/aws/spring-cloud-caching-redis/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'org.springframework.boot' version '2.4.5' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'io.reflectoring' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-cache' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'io.awspring.cloud:spring-cloud-starter-aws' +// implementation 'com.amazonaws:aws-java-sdk-cloudformation' + implementation 'com.amazonaws:aws-java-sdk-elasticache' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.h2database:h2' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +dependencyManagement { + imports { + mavenBom 'io.awspring.cloud:spring-cloud-aws-dependencies:2.3.1' + } +} + +test { + useJUnitPlatform() +} diff --git a/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.jar b/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.jar differ diff --git a/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.properties b/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/aws/spring-cloud-caching-redis/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/aws/spring-cloud-caching-redis/gradlew b/aws/spring-cloud-caching-redis/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/aws/spring-cloud-caching-redis/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/aws/spring-cloud-caching-redis/gradlew.bat b/aws/spring-cloud-caching-redis/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/aws/spring-cloud-caching-redis/gradlew.bat @@ -0,0 +1,89 @@ +@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=. +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%" == "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%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/aws/spring-cloud-caching-redis/settings.gradle b/aws/spring-cloud-caching-redis/settings.gradle new file mode 100644 index 0000000..43a95ed --- /dev/null +++ b/aws/spring-cloud-caching-redis/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'spring-cloud-redis' diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/SpringCloudRedisApplication.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/SpringCloudRedisApplication.java new file mode 100644 index 0000000..1975781 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/SpringCloudRedisApplication.java @@ -0,0 +1,12 @@ +package io.reflectoring.springcloudredis; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringCloudRedisApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudRedisApplication.class, args); + } +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/configuration/EnableCache.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/configuration/EnableCache.java new file mode 100644 index 0000000..5655b53 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/configuration/EnableCache.java @@ -0,0 +1,10 @@ +package io.reflectoring.springcloudredis.configuration; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class EnableCache { + +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/ProductController.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/ProductController.java new file mode 100644 index 0000000..e08f41b --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/ProductController.java @@ -0,0 +1,41 @@ +package io.reflectoring.springcloudredis.controller; + +import io.reflectoring.springcloudredis.entity.Product; +import io.reflectoring.springcloudredis.model.ProductInput; +import io.reflectoring.springcloudredis.service.ProductService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +@Slf4j(topic = "PRODUCT_CONTROLLER") +@RestController +@RequestMapping("/product") +@AllArgsConstructor +public class ProductController { + private final ProductService productService; + + @GetMapping("/{id}") + public Product getProduct(@PathVariable String id){ + return productService.getProduct(id); + } + + @DeleteMapping("/{id}") + public void deleteProduct(@PathVariable String id){ + productService.deleteProduct(id); + } + + @PutMapping("/{id}") + public Product updateProduct(@PathVariable String id, @RequestBody @Valid ProductInput input){ + return productService.updateProduct(id, input); + } + + @PostMapping + public Product addProduct(@RequestBody @Valid ProductInput input){ + return productService.addProduct(input); + } + + +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/UserController.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/UserController.java new file mode 100644 index 0000000..9f061c2 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/controller/UserController.java @@ -0,0 +1,24 @@ +package io.reflectoring.springcloudredis.controller; + +import io.reflectoring.springcloudredis.entity.User; +import io.reflectoring.springcloudredis.service.UserService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j(topic = "PRODUCT_CONTROLLER") +@RestController +@RequestMapping("/user") +@AllArgsConstructor +public class UserController { + + private final UserService userService; + + @GetMapping("/{id}") + public User getUser(@PathVariable String id) { + return userService.getUser(id); + } +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Category.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Category.java new file mode 100644 index 0000000..fd1fd49 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Category.java @@ -0,0 +1,12 @@ +package io.reflectoring.springcloudredis.entity; + +import java.io.Serializable; + +public enum Category implements Serializable { + MOBILE, + TV_APPLIANCES, + MEN_FASHION, + WOMEN_FASHION, + BOOKS, + BEAUTY +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Product.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Product.java new file mode 100644 index 0000000..b62060a --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/Product.java @@ -0,0 +1,44 @@ +package io.reflectoring.springcloudredis.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +public class Product implements Serializable { + @Id + private String id; + + private String name; + + private Double price; + + private LocalDateTime manufacturingDate; + + @Transient + private Object data; + + private Double weight; + + @Embedded + private Dimension dimension; + + @Enumerated(EnumType.STRING) + private Category category; + + @Embeddable + @Getter + @Setter + public static class Dimension implements Serializable { + private Double height; + + private Double width; + } + + +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/User.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/User.java new file mode 100644 index 0000000..38ecef3 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/entity/User.java @@ -0,0 +1,20 @@ +package io.reflectoring.springcloudredis.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.Id; +import java.io.Serializable; + +@Entity +@Getter +@Setter +public class User implements Serializable { + @Id + private String id; + + private String firstName; + + private String lastName; +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/model/ProductInput.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/model/ProductInput.java new file mode 100644 index 0000000..718e680 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/model/ProductInput.java @@ -0,0 +1,23 @@ +package io.reflectoring.springcloudredis.model; + +import io.reflectoring.springcloudredis.entity.Category; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Getter +@Setter +public class ProductInput { + + @NotBlank + private String name; + + @NotNull + private Double price; + + private Double weight; + + private Category category; +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/ProductRepository.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/ProductRepository.java new file mode 100644 index 0000000..7120831 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/ProductRepository.java @@ -0,0 +1,9 @@ +package io.reflectoring.springcloudredis.repository; + +import io.reflectoring.springcloudredis.entity.Product; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductRepository extends JpaRepository { +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/UserRepository.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/UserRepository.java new file mode 100644 index 0000000..ee78a1f --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/repository/UserRepository.java @@ -0,0 +1,9 @@ +package io.reflectoring.springcloudredis.repository; + +import io.reflectoring.springcloudredis.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/ProductService.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/ProductService.java new file mode 100644 index 0000000..e5ded28 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/ProductService.java @@ -0,0 +1,52 @@ +package io.reflectoring.springcloudredis.service; + +import io.reflectoring.springcloudredis.entity.Category; +import io.reflectoring.springcloudredis.entity.Product; +import io.reflectoring.springcloudredis.model.ProductInput; +import io.reflectoring.springcloudredis.repository.ProductRepository; +import lombok.AllArgsConstructor; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +@Service +@AllArgsConstructor +@CacheConfig(cacheNames = "product-cache") +public class ProductService { + private final ProductRepository repository; + + @Cacheable + public Product getProduct(String id) { + return repository.findById(id).orElseThrow(()-> + new RuntimeException("No such product found with id")); + } + + public Product addProduct(ProductInput productInput){ + var product = new Product(); + product.setName(productInput.getName()); + product.setPrice(productInput.getPrice()); + product.setWeight(product.getWeight()); + product.setCategory(Objects.isNull(productInput.getCategory())? Category.BOOKS: productInput.getCategory()); + return repository.save(product); + } + + @CacheEvict + public void deleteProduct(String id) { + repository.deleteById(id); + } + + @CachePut(key = "#id") + public Product updateProduct(String id, ProductInput productInput) { + var product = new Product(); + product.setId(id); + product.setName(productInput.getName()); + product.setPrice(productInput.getPrice()); + product.setWeight(product.getWeight()); + product.setCategory(Objects.isNull(productInput.getCategory())? Category.BOOKS: productInput.getCategory()); + return repository.save(product); + } +} diff --git a/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/UserService.java b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/UserService.java new file mode 100644 index 0000000..0a6f64e --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/java/io/reflectoring/springcloudredis/service/UserService.java @@ -0,0 +1,22 @@ +package io.reflectoring.springcloudredis.service; + +import io.reflectoring.springcloudredis.entity.User; +import io.reflectoring.springcloudredis.repository.UserRepository; +import lombok.AllArgsConstructor; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +@AllArgsConstructor +@CacheConfig(cacheNames = "product-cache") +public class UserService { + + private final UserRepository repository; + + @Cacheable + public User getUser(String id){ + return repository.findById(id).orElseThrow(()-> + new RuntimeException("No such user found with id")); + } +} diff --git a/aws/spring-cloud-caching-redis/src/main/resources/application.yaml b/aws/spring-cloud-caching-redis/src/main/resources/application.yaml new file mode 100644 index 0000000..ac41674 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/resources/application.yaml @@ -0,0 +1,24 @@ +spring: + datasource: + initialization-mode: always + application: + name: spring-cloud-redis + jpa: + show-sql: true + generate-ddl: true + hibernate: + ddl-auto: create-drop +#cloud: +# aws: +# stack: +# name: spring-cache-2 +cloud: + aws: + elasticache: + clusters: + - + name: product-cache + expiration: 100 + - + name: user-cache + expiration: 6000 diff --git a/aws/spring-cloud-caching-redis/src/main/resources/data.sql b/aws/spring-cloud-caching-redis/src/main/resources/data.sql new file mode 100644 index 0000000..eb6f4b5 --- /dev/null +++ b/aws/spring-cloud-caching-redis/src/main/resources/data.sql @@ -0,0 +1,39 @@ +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_1', 'one plus 8T', 100, now(), 2.2, 3.4, 5.4, 'MOBILE'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_2', 'Samsung 100 S', 200, now(), 5.6, 7.9, 4.3, 'MOBILE'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_3', 'Armani jacket size 32', 1100, now(), 1.3, 10.4, 29.5, 'MEN_FASHION'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_4', 'Zara purse', 500, now(), 3.7, 50.6, 70.6, 'WOMEN_FASHION'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_5', 'Sony Bravia ', 2000, now(), 25.5, 48.8, 25.9, 'TV_APPLIANCES'); + +insert into product +(id, name, price, manufacturing_date, weight, height, width, category) +values +('prod_6', 'zara jacket green color', 1500, now(), 1.3, 10.4, 29.5, 'MEN_FASHION'); + +insert into user +(id, first_name, last_name) +values +('user_1', 'john', 'doe'); + +insert into user +(id, first_name, last_name) +values +('user_2', 'richard', 'mayor'); diff --git a/build-all.sh b/build-all.sh index 065929d..4af4398 100755 --- a/build-all.sh +++ b/build-all.sh @@ -82,14 +82,31 @@ build_maven_module() { } } -if [[ "$MODULE" == "module5" ]] +if [[ "$MODULE" == "module6" ]] then # ADD NEW MODULES HERE # (add new modules above the rest so you get quicker feedback if it fails) build maven_module "aws/springcloudwatch" build maven_module "aws/springcloudses" build maven_module "spring-boot/spring-boot-camel" - build maven_module "aws/aws-dynamodb" + build_maven_module "logging/structured-logging" + build_maven_module "spring-boot/zero-downtime" + build_maven_module "resilience4j/springboot-resilience4j" + build_maven_module "spring-boot/feature-flags" + build_gradle_module "aws/spring-cloud-caching-redis" + build_maven_module "logging/spring-boot" + build_maven_module "logging/logback" + build_maven_module "logging/log4j" + + echo "" + echo "+++" + echo "+++ MODULE 6 SUCCESSFUL" + echo "+++" +fi + +if [[ "$MODULE" == "module5" ]] +then + build_maven_module "aws/aws-dynamodb" build_maven_module "spring-boot/spring-boot-testconfiguration" build_maven_module "aws/springcloudrds" build_maven_module "aws/springcloudsqs" @@ -188,7 +205,6 @@ fi if [[ "$MODULE" == "module4" ]] then - build_maven_module "core-java/threaddump" build_gradle_module "spring-boot/mocking" build_gradle_module "spring-boot/modular" build_gradle_module "spring-boot/paging" diff --git a/logging/log4j/.mvn/wrapper/MavenWrapperDownloader.java b/logging/log4j/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..b901097 --- /dev/null +++ b/logging/log4j/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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 + * + * 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/logging/log4j/.mvn/wrapper/maven-wrapper.properties b/logging/log4j/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/logging/log4j/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/logging/log4j/mvnw b/logging/log4j/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/logging/log4j/mvnw @@ -0,0 +1,310 @@ +#!/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 +# +# 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/logging/log4j/mvnw.cmd b/logging/log4j/mvnw.cmd new file mode 100644 index 0000000..8611571 --- /dev/null +++ b/logging/log4j/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/logging/log4j/pom.xml b/logging/log4j/pom.xml new file mode 100644 index 0000000..82510fb --- /dev/null +++ b/logging/log4j/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + io.reflectoring + logging-log4j + 1.0-SNAPSHOT + + + 11 + 11 + + + + + + org.apache.logging.log4j + log4j-api + 2.14.1 + + + org.apache.logging.log4j + log4j-core + 2.14.1 + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.14.1 + + + io.logz.log4j2 + logzio-log4j2-appender + 1.0.12 + + + \ No newline at end of file diff --git a/logging/log4j/src/main/java/io/reflectoring/Main.java b/logging/log4j/src/main/java/io/reflectoring/Main.java new file mode 100644 index 0000000..4e97416 --- /dev/null +++ b/logging/log4j/src/main/java/io/reflectoring/Main.java @@ -0,0 +1,17 @@ +package io.reflectoring; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main { + + public static void main(String[] args) { + Logger logger = LoggerFactory.getLogger(Main.class); + + logger.debug("This is a debug message"); + logger.info("This is an info message"); + logger.warn("This is a warn message"); + logger.error("This is an error message"); + } + +} diff --git a/logging/log4j/src/main/resources/log4j2.xml b/logging/log4j/src/main/resources/log4j2.xml new file mode 100644 index 0000000..553aa02 --- /dev/null +++ b/logging/log4j/src/main/resources/log4j2.xml @@ -0,0 +1,20 @@ + + + + + + + + + ${env:LOGZIO_TOKEN} + https://listener.logz.io:8071 + log4j-example-app + + + + + + + + + diff --git a/logging/logback/.mvn/wrapper/MavenWrapperDownloader.java b/logging/logback/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..b901097 --- /dev/null +++ b/logging/logback/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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 + * + * 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/logging/logback/.mvn/wrapper/maven-wrapper.properties b/logging/logback/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/logging/logback/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/logging/logback/mvnw b/logging/logback/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/logging/logback/mvnw @@ -0,0 +1,310 @@ +#!/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 +# +# 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/logging/logback/mvnw.cmd b/logging/logback/mvnw.cmd new file mode 100644 index 0000000..8611571 --- /dev/null +++ b/logging/logback/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/logging/logback/pom.xml b/logging/logback/pom.xml new file mode 100644 index 0000000..4574a10 --- /dev/null +++ b/logging/logback/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + io.reflectoring + logging-logback + 1.0-SNAPSHOT + + + 11 + 11 + + + + + + ch.qos.logback + logback-classic + 1.2.3 + + + io.logz.logback + logzio-logback-appender + 1.0.24 + + + diff --git a/logging/logback/src/main/java/io/reflectoring/Main.java b/logging/logback/src/main/java/io/reflectoring/Main.java new file mode 100644 index 0000000..4e97416 --- /dev/null +++ b/logging/logback/src/main/java/io/reflectoring/Main.java @@ -0,0 +1,17 @@ +package io.reflectoring; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main { + + public static void main(String[] args) { + Logger logger = LoggerFactory.getLogger(Main.class); + + logger.debug("This is a debug message"); + logger.info("This is an info message"); + logger.warn("This is a warn message"); + logger.error("This is an error message"); + } + +} diff --git a/logging/logback/src/main/resources/logback.xml b/logging/logback/src/main/resources/logback.xml new file mode 100644 index 0000000..0027f97 --- /dev/null +++ b/logging/logback/src/main/resources/logback.xml @@ -0,0 +1,21 @@ + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + ${LOGZIO_TOKEN} + https://listener.logz.io:8071 + logback-example-app + + + + + + diff --git a/logging/spring-boot/.gitignore b/logging/spring-boot/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/logging/spring-boot/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/logging/spring-boot/.mvn/wrapper/MavenWrapperDownloader.java b/logging/spring-boot/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..e76d1f3 --- /dev/null +++ b/logging/spring-boot/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/logging/spring-boot/.mvn/wrapper/maven-wrapper.jar b/logging/spring-boot/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/logging/spring-boot/.mvn/wrapper/maven-wrapper.jar differ diff --git a/logging/spring-boot/.mvn/wrapper/maven-wrapper.properties b/logging/spring-boot/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffdc10e --- /dev/null +++ b/logging/spring-boot/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/logging/spring-boot/mvnw b/logging/spring-boot/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/logging/spring-boot/mvnw @@ -0,0 +1,310 @@ +#!/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 +# +# 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/logging/spring-boot/mvnw.cmd b/logging/spring-boot/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/logging/spring-boot/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. 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, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/logging/spring-boot/pom.xml b/logging/spring-boot/pom.xml new file mode 100644 index 0000000..29f47e1 --- /dev/null +++ b/logging/spring-boot/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.0 + + + io.reflectoring + logging-demo + 0.0.1-SNAPSHOT + logging-demo + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + io.logz.logback + logzio-logback-appender + 1.0.24 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/LoggingDemoApplication.java b/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/LoggingDemoApplication.java new file mode 100644 index 0000000..c7223b4 --- /dev/null +++ b/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/LoggingDemoApplication.java @@ -0,0 +1,13 @@ +package io.reflectoring.loggingdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LoggingDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(LoggingDemoApplication.class, args); + } + +} diff --git a/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/StartupLogger.java b/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/StartupLogger.java new file mode 100644 index 0000000..62d022b --- /dev/null +++ b/logging/spring-boot/src/main/java/io/reflectoring/loggingdemo/StartupLogger.java @@ -0,0 +1,21 @@ +package io.reflectoring.loggingdemo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Component +public class StartupLogger implements ApplicationListener { + + private static final Logger logger = LoggerFactory.getLogger(StartupLogger.class); + + @Override + public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { + logger.debug("This is a debug message"); + logger.info("This is an info message"); + logger.warn("This is a warn message"); + logger.error("This is an error message"); + } +} diff --git a/logging/spring-boot/src/main/resources/application.yml b/logging/spring-boot/src/main/resources/application.yml new file mode 100644 index 0000000..e7410e8 --- /dev/null +++ b/logging/spring-boot/src/main/resources/application.yml @@ -0,0 +1,2 @@ +logzio: + token: ${LOGZIO_TOKEN} \ No newline at end of file diff --git a/logging/spring-boot/src/main/resources/logback-spring.xml b/logging/spring-boot/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..0a861ec --- /dev/null +++ b/logging/spring-boot/src/main/resources/logback-spring.xml @@ -0,0 +1,44 @@ + + + + + + + + + ${logzioToken} + https://listener.logz.io:8071 + spring-boot-example-app + + + + + + %cyan(%d{ISO8601}) %highlight(%-5level) [%blue(%-30t)] %yellow(%C{1.}): %msg%n%throwable + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logging/structured-logging/.gitignore b/logging/structured-logging/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/logging/structured-logging/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/logging/structured-logging/.mvn/wrapper/MavenWrapperDownloader.java b/logging/structured-logging/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..e76d1f3 --- /dev/null +++ b/logging/structured-logging/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/logging/structured-logging/.mvn/wrapper/maven-wrapper.jar b/logging/structured-logging/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/logging/structured-logging/.mvn/wrapper/maven-wrapper.jar differ diff --git a/logging/structured-logging/.mvn/wrapper/maven-wrapper.properties b/logging/structured-logging/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffdc10e --- /dev/null +++ b/logging/structured-logging/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/logging/structured-logging/README.md b/logging/structured-logging/README.md new file mode 100644 index 0000000..5beb2ae --- /dev/null +++ b/logging/structured-logging/README.md @@ -0,0 +1,3 @@ +1. Start the app with `./mvn spring-boot:run`. +2. Go to `http://localhost:8080/hello`. +3. Log in as `user` with the password that is printed in the log output of the app. \ No newline at end of file diff --git a/logging/structured-logging/mvnw b/logging/structured-logging/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/logging/structured-logging/mvnw @@ -0,0 +1,310 @@ +#!/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 +# +# 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/logging/structured-logging/mvnw.cmd b/logging/structured-logging/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/logging/structured-logging/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. 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, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/logging/structured-logging/pom.xml b/logging/structured-logging/pom.xml new file mode 100644 index 0000000..fc962de --- /dev/null +++ b/logging/structured-logging/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.3 + + + io.reflectoring + structured-logging + 0.0.1-SNAPSHOT + structured-logging + Demo project for Spring Boot + + 11 + + + + + + org.springframework.cloud + spring-cloud-dependencies + 2020.0.3 + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + io.logz.logback + logzio-logback-appender + 1.0.24 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/logging/structured-logging/src/main/java/io/reflectoring/CombobulatorException.java b/logging/structured-logging/src/main/java/io/reflectoring/CombobulatorException.java new file mode 100644 index 0000000..896a3ab --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/CombobulatorException.java @@ -0,0 +1,5 @@ +package io.reflectoring; + +public class CombobulatorException extends RuntimeException { + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/FluxCompensatorException.java b/logging/structured-logging/src/main/java/io/reflectoring/FluxCompensatorException.java new file mode 100644 index 0000000..a095b0a --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/FluxCompensatorException.java @@ -0,0 +1,5 @@ +package io.reflectoring; + +public class FluxCompensatorException extends RuntimeException { + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/MaintenanceWindowException.java b/logging/structured-logging/src/main/java/io/reflectoring/MaintenanceWindowException.java new file mode 100644 index 0000000..1f54725 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/MaintenanceWindowException.java @@ -0,0 +1,5 @@ +package io.reflectoring; + +public class MaintenanceWindowException extends RuntimeException { + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/ThingyException.java b/logging/structured-logging/src/main/java/io/reflectoring/ThingyException.java new file mode 100644 index 0000000..3eff510 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/ThingyException.java @@ -0,0 +1,5 @@ +package io.reflectoring; + +public class ThingyException extends RuntimeException { + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/DomainService.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/DomainService.java new file mode 100644 index 0000000..7370b50 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/DomainService.java @@ -0,0 +1,36 @@ +package io.reflectoring.structuredlogging; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.Instant; + +@Component +public class DomainService { + + private static final Logger logger = LoggerFactory.getLogger(DomainService.class); + + String hello(){ + logger.info("log event from the domain service"); + return "hello"; + } + + String callThirdPartyService() throws InterruptedException { + logger.info("log event from the domain service"); + Instant start = Instant.now(); + + Thread.sleep(2000); // simulating an expensive operation + + Duration duration = Duration.between(start, Instant.now()); + + MDC.put("thirdPartyCallDuration", String.valueOf(duration.getNano())); + logger.info("call to third party service successful!"); + MDC.remove("thirdPartyCallDuration"); + + return "hello"; + } + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/LoggingInterceptor.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/LoggingInterceptor.java new file mode 100644 index 0000000..4f3220b --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/LoggingInterceptor.java @@ -0,0 +1,42 @@ +package io.reflectoring.structuredlogging; + +import org.slf4j.MDC; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class LoggingInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + + if (principal instanceof UserDetails) { + String username = ((UserDetails)principal).getUsername(); + MDC.put("username", username); + } else { + String username = principal.toString(); + MDC.put("username", username); + } + + + if (request.getHeader("X-CUSTOM-HEADER") != null) { + MDC.put("codePath", "3rdParty"); + } else { + MDC.put("codePath", "user"); + } + + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { + MDC.remove("username"); + MDC.remove("codePath"); + } +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/StructuredLoggingApplication.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/StructuredLoggingApplication.java new file mode 100644 index 0000000..69fdbbb --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/StructuredLoggingApplication.java @@ -0,0 +1,15 @@ +package io.reflectoring.structuredlogging; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableScheduling +public class StructuredLoggingApplication { + + public static void main(String[] args) { + SpringApplication.run(StructuredLoggingApplication.class, args); + } + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/Timer.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/Timer.java new file mode 100644 index 0000000..f38c7b6 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/Timer.java @@ -0,0 +1,28 @@ +package io.reflectoring.structuredlogging; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Timer { + + private final DomainService domainService; + + private static final Logger logger = LoggerFactory.getLogger(Timer.class); + + public Timer(DomainService domainService) { + this.domainService = domainService; + } + + @Scheduled(fixedDelay = 5000) + void scheduledHello() { + MDC.put("codePath", "timer"); + logger.info("log event from timer"); + domainService.hello(); + MDC.remove("codePath"); + } + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebConfigurer.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebConfigurer.java new file mode 100644 index 0000000..8250a1c --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebConfigurer.java @@ -0,0 +1,14 @@ +package io.reflectoring.structuredlogging; + +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Component +public class WebConfigurer implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new LoggingInterceptor()); + } +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebController.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebController.java new file mode 100644 index 0000000..ebcf507 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebController.java @@ -0,0 +1,57 @@ +package io.reflectoring.structuredlogging; + +import io.reflectoring.CombobulatorException; +import io.reflectoring.FluxCompensatorException; +import io.reflectoring.MaintenanceWindowException; +import io.reflectoring.ThingyException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Random; + + +@RestController +public class WebController { + + private final DomainService domainService; + + private static final Logger logger = LoggerFactory.getLogger(WebController.class); + + private final Random random = new Random(); + + public WebController(DomainService domainService) { + this.domainService = domainService; + } + + @GetMapping("/hello") + String helloWorld() { + logger.info("log event from the web controller"); + return domainService.hello(); + } + + @GetMapping("/thirdparty") + String thirdparty() throws InterruptedException { + logger.info("log event from the web controller"); + return domainService.callThirdPartyService(); + } + + @GetMapping("/exception") + String error() { + logger.info("log event from the web controller"); + + int r = random.nextInt(101); + + if (r <= 10) { + throw new CombobulatorException(); + } else if (r <= 30) { + throw new FluxCompensatorException(); + } else if (r <= 80) { + throw new ThingyException(); + } else { + throw new MaintenanceWindowException(); + } + } + +} diff --git a/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebExceptionHandler.java b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebExceptionHandler.java new file mode 100644 index 0000000..d281319 --- /dev/null +++ b/logging/structured-logging/src/main/java/io/reflectoring/structuredlogging/WebExceptionHandler.java @@ -0,0 +1,32 @@ +package io.reflectoring.structuredlogging; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public class WebExceptionHandler { + + private static final Logger logger = LoggerFactory.getLogger(WebExceptionHandler.class); + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public void internalServerError(Exception e){ + MDC.put("rootCause", getRootCause(e).getClass().getName()); + logger.error("returning 500 (internal server error).", e); + MDC.remove("rootCause"); + } + + private Throwable getRootCause(Exception e){ + Throwable rootCause = e; + while(e.getCause() != null && rootCause.getCause() != rootCause){ + rootCause = e.getCause(); + } + return rootCause; + } + +} diff --git a/logging/structured-logging/src/main/resources/application.properties b/logging/structured-logging/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/logging/structured-logging/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/logging/structured-logging/src/main/resources/logback.xml b/logging/structured-logging/src/main/resources/logback.xml new file mode 100644 index 0000000..69d4fca --- /dev/null +++ b/logging/structured-logging/src/main/resources/logback.xml @@ -0,0 +1,23 @@ + + + + + + + %cyan(%d{ISO8601}) %highlight(%-5level) [%blue(%-30t)] [%blue(%X{codePath})] [%blue(%X{rootCause})] [%X{traceId:-},%X{spanId:-}] [%X{thirdPartyCallDuration}] %yellow(%C{1.}): %msg%n%throwable + + + + + + ${LOGZIO_TOKEN} + https://listener.logz.io:8071 + structured-logging-application + + + + + + + + \ No newline at end of file diff --git a/logging/structured-logging/src/test/java/io/reflectoring/structuredlogging/StructuredLoggingApplicationTests.java b/logging/structured-logging/src/test/java/io/reflectoring/structuredlogging/StructuredLoggingApplicationTests.java new file mode 100644 index 0000000..3f25ff8 --- /dev/null +++ b/logging/structured-logging/src/test/java/io/reflectoring/structuredlogging/StructuredLoggingApplicationTests.java @@ -0,0 +1,13 @@ +package io.reflectoring.structuredlogging; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class StructuredLoggingApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/resilience4j/springboot-resilience4j/.mvn/wrapper/MavenWrapperDownloader.java b/resilience4j/springboot-resilience4j/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..b901097 --- /dev/null +++ b/resilience4j/springboot-resilience4j/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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 + * + * 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.jar b/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.jar differ diff --git a/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.properties b/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/resilience4j/springboot-resilience4j/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/resilience4j/springboot-resilience4j/README.md b/resilience4j/springboot-resilience4j/README.md new file mode 100644 index 0000000..1c89564 --- /dev/null +++ b/resilience4j/springboot-resilience4j/README.md @@ -0,0 +1,8 @@ +# Retry with Resilience4J + +Run the SpringbootResilience4jApplication program + +## Blog posts + +* [Implementing Retry with Spring_Boot_Resilience4j](https://reflectoring.io/retry-with-springboot-resilience4j/) +* [Implementing Rate Limiting with Spring_Boot_Resilience4j](https://reflectoring.io/rate-limiting-with-springboot-resilience4j/) diff --git a/resilience4j/springboot-resilience4j/mvnw b/resilience4j/springboot-resilience4j/mvnw new file mode 100755 index 0000000..41c0f0c --- /dev/null +++ b/resilience4j/springboot-resilience4j/mvnw @@ -0,0 +1,310 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/resilience4j/springboot-resilience4j/mvnw.cmd b/resilience4j/springboot-resilience4j/mvnw.cmd new file mode 100644 index 0000000..8611571 --- /dev/null +++ b/resilience4j/springboot-resilience4j/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/resilience4j/springboot-resilience4j/pom.xml b/resilience4j/springboot-resilience4j/pom.xml new file mode 100644 index 0000000..c692905 --- /dev/null +++ b/resilience4j/springboot-resilience4j/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.1 + + + io.reflectoring.resilience4j.springboot + springboot-resilience4j + 0.0.1-SNAPSHOT + springboot-resilience4j + https://reflectoring.io + Spring Boot & Resilience4j + + + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-actuator + 2.4.1 + + + + + org.springframework.boot + spring-boot-starter-aop + 2.4.1 + + + + + io.github.resilience4j + resilience4j-spring-boot2 + 1.7.0 + + + + org.projectlombok + lombok + 1.18.12 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimiterExamplesRunner.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimiterExamplesRunner.java new file mode 100644 index 0000000..9318825 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimiterExamplesRunner.java @@ -0,0 +1,131 @@ +package io.reflectoring.resilience4j.springboot; + +import io.reflectoring.resilience4j.springboot.model.SearchRequest; +import java.time.Duration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RateLimiterExamplesRunner { + + @Autowired + private RateLimitingService service; + + public static void main(String[] args) { + RateLimiterExamplesRunner runner = new RateLimiterExamplesRunner(); + runner.run(); + } + + public void run() { + System.out.println("Running ratelimiter examples"); + + System.out.println("----------------------------- basicExample ------------------------------------------"); + basicExample(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("----------------------------- timeoutExample ------------------------------------------"); + timeoutExample(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("------------------------------ multipleLimits_2rps_40rpm_sequential -----------------------------------------"); + multipleLimits_2rps_40rpm_sequential(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("------------------------------- changeLimitsExample ----------------------------------------"); + changeLimitsExample(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("------------------------------- retryAndRateLimit ----------------------------------------"); + retryAndRateLimit(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("------------------------------ rateLimiterEvents -----------------------------------------"); + rateLimiterEvents(); + System.out.println("-----------------------------------------------------------------------"); + + System.out.println("----------------------------- fallbackExample ------------------------------------------"); + fallbackExample(); + System.out.println("-----------------------------------------------------------------------"); + } + + private void rateLimiterEvents() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + + try { + System.out.println(service.rateLimiterEventsExample(request)); + System.out.println(service.rateLimiterEventsExample(request)); + } + catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + private void retryAndRateLimit() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + + System.out.println(service.retryAndRateLimit(request)); + System.out.println(service.retryAndRateLimit(request)); + } + + private void changeLimitsExample() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + + for (int i=0; i<6; i++) { + System.out.println(service.changeLimitsExample(request)); + } + + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + service.updateRateLimits("changeLimitsExample", 2, Duration.ofSeconds(2)); + System.out.println("Rate limits changed"); + + for (int i=0; i<6; i++) { + System.out.println(service.changeLimitsExample(request)); + } + } + + private void multipleLimits_2rps_40rpm_sequential() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + for (int i=0; i<45; i++) { + try { + System.out.println(service.multipleRateLimitsExample(request)); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void timeoutExample() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + try { + for (int i=0; i<3; i++) { + System.out.println(service.timeoutExample(request)); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private void basicExample() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + for (int i=0; i<4; i++) { + try { + System.out.println(service.basicExample(request)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void fallbackExample() { + SearchRequest request = new SearchRequest("NYC", "LAX", "08/15/2021"); + System.out.println(service.fallbackExample(request)); + System.out.println(service.fallbackExample(request)); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimitingService.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimitingService.java new file mode 100644 index 0000000..3b93efb --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RateLimitingService.java @@ -0,0 +1,128 @@ +package io.reflectoring.resilience4j.springboot; + +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; +import io.github.resilience4j.ratelimiter.annotation.RateLimiter; +import io.github.resilience4j.retry.RetryRegistry; +import io.github.resilience4j.retry.annotation.Retry; +import io.reflectoring.resilience4j.springboot.model.Flight; +import io.reflectoring.resilience4j.springboot.model.SearchRequest; +import io.reflectoring.resilience4j.springboot.services.FlightSearchService; +import java.time.Duration; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +@Service +public class RateLimitingService { + @Autowired + private FlightSearchService remoteSearchService; + + @Autowired + private RPMRateLimitedFlightSearchSearch rpmRateLimitedFlightSearchSearch; + + @Autowired + private RateLimiterRegistry registry; + + @Autowired + private RetryRegistry retryRegistry; + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss SSS"); + + @RateLimiter(name = "basicExample") + List basicExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @RateLimiter(name = "timeoutExample") + List timeoutExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @RateLimiter(name = "multipleRateLimiters_rps_limiter") + List multipleRateLimitsExample(SearchRequest request) { + return rpmRateLimitedFlightSearchSearch.searchFlights(request, remoteSearchService); + } + +// doesn't work - @RateLimiter is not a repeatable annotation +// @RateLimiter(name = "multipleRateLimiters_rps_limiter") +// @RateLimiter(name = "multipleRateLimiters_rpm_limiter") +// List multipleRateLimitsExample(SearchRequest request) { +// return remoteSearchService.searchFlights(request, remoteSearchService); +// } + +// doesn't work - calls within a Spring bean don't go thru the Spring proxy +// @RateLimiter(name = "multipleRateLimiters_rps_limiter") +// List rpsLimitedSearch(SearchRequest request) { +// return rpmLimitedSearch(request, remoteSearchService); +// } + +// @RateLimiter(name = "multipleRateLimiters_rpm_limiter") +// List rpmLimitedSearch(SearchRequest request) { +// return remoteSearchService.searchFlights(request, remoteSearchService); +// } + + + @RateLimiter(name = "changeLimitsExample") + public List changeLimitsExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @Retry(name = "retryAndRateLimitExample") + @RateLimiter(name = "retryAndRateLimitExample") + public List retryAndRateLimit(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @RateLimiter(name = "rateLimiterEventsExample") + public List rateLimiterEventsExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + public void updateRateLimits(String rateLimiterName, int newLimitForPeriod, Duration newTimeoutDuration) { + io.github.resilience4j.ratelimiter.RateLimiter limiter = registry.rateLimiter(rateLimiterName); + limiter.changeLimitForPeriod(newLimitForPeriod); + limiter.changeTimeoutDuration(newTimeoutDuration); + } + + @RateLimiter(name = "fallbackExample", fallbackMethod = "localCacheFlightSearch") + public List fallbackExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + private List localCacheFlightSearch(SearchRequest request, RequestNotPermitted rnp) { + System.out.println("Returning search results from cache"); + return Arrays.asList( + new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo())); + } + + @PostConstruct + public void postConstruct() { + io.github.resilience4j.retry.Retry.EventPublisher retryEventPublisher = retryRegistry + .retry("retryAndRateLimitExample") + .getEventPublisher(); + + retryEventPublisher.onRetry(System.out::println); + retryEventPublisher.onSuccess(System.out::println); + + io.github.resilience4j.ratelimiter.RateLimiter.EventPublisher eventPublisher = registry + .rateLimiter("rateLimiterEventsExample") + .getEventPublisher(); + + eventPublisher.onSuccess(System.out::println); + eventPublisher.onFailure(System.out::println); + } +} + +@Component +class RPMRateLimitedFlightSearchSearch { + @RateLimiter(name = "multipleRateLimiters_rpm_limiter") + List searchFlights(SearchRequest request, FlightSearchService remoteSearchService) { + return remoteSearchService.searchFlights(request); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryExamplesRunner.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryExamplesRunner.java new file mode 100644 index 0000000..76a84bd --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryExamplesRunner.java @@ -0,0 +1,122 @@ +package io.reflectoring.resilience4j.springboot; + +import io.reflectoring.resilience4j.springboot.model.Flight; +import io.reflectoring.resilience4j.springboot.model.SearchRequest; +import io.reflectoring.resilience4j.springboot.model.SearchResponse; +import io.reflectoring.resilience4j.springboot.services.failures.FailNTimes; +import io.reflectoring.resilience4j.springboot.services.failures.FailNTimesCheckedException; +import java.io.IOException; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RetryExamplesRunner { + + @Autowired + private RetryingService service; + + public static void main(String[] args) { + RetryExamplesRunner runner = new RetryExamplesRunner(); + runner.run(); + } + + public void run() { + System.out.println("Running retry examples"); + + System.out.println( + "------------------------- basicExample ---------------------------------------------"); + basicExample(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "------------------------- checkedExceptionExample ---------------------------------------------"); + checkedExceptionExample(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "--------------------------- predicateExample -------------------------------------------"); + predicateExample(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "---------------------------- intervalFunction_Random ------------------------------------------"); + intervalFunction_Random(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "----------------------------- intervalFunction_Exponential -----------------------------------------"); + intervalFunction_Exponential(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "----------------------------- retryEventsExample -----------------------------------------"); + retryEventsExample(); + System.out.println("----------------------------------------------------------------------"); + + System.out.println( + "----------------------------- fallbackExample -----------------------------------------"); + fallbackExample(); + System.out.println("----------------------------------------------------------------------"); + } + + private void retryEventsExample() { + service.setPotentialFailure(new FailNTimes(2)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + List flights = service.loggedRetryExample(request); + System.out.println(flights); + } + + private void intervalFunction_Exponential() { + service.setPotentialFailure(new FailNTimes(5)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + List flights = service.intervalFunctionExponential(request); + System.out.println(flights); + } + + private void intervalFunction_Random() { + service.setPotentialFailure(new FailNTimes(2)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + List flights = service.intervalFunctionRandom(request); + System.out.println(flights); + } + + private void predicateExample() { + service.setPotentialFailure(new FailNTimes(1)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + try { + SearchResponse response = service.predicateExample(request); + List flights = response.getFlights(); + System.out.println(flights); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void checkedExceptionExample() { + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + service.setPotentialFailureCheckedException(new FailNTimesCheckedException(2)); + + try { + List flights = service.searchFlightsThrowingException(request); + System.out.println(flights); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + private void basicExample() { + service.setPotentialFailure(new FailNTimes(1)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + List flights = service.basicExample(request); + System.out.println(flights); + } + + private void fallbackExample() { + service.setPotentialFailure(new FailNTimes(4)); + SearchRequest request = new SearchRequest("NYC", "LAX", "07/31/2021"); + List flights = service.fallbackExample(request); + System.out.println(flights); + + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryingService.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryingService.java new file mode 100644 index 0000000..25f0fa7 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/RetryingService.java @@ -0,0 +1,91 @@ +package io.reflectoring.resilience4j.springboot; + +import io.github.resilience4j.retry.RetryRegistry; +import io.github.resilience4j.retry.annotation.Retry; +import io.reflectoring.resilience4j.springboot.model.Flight; +import io.reflectoring.resilience4j.springboot.model.SearchRequest; +import io.reflectoring.resilience4j.springboot.model.SearchResponse; +import io.reflectoring.resilience4j.springboot.services.FlightSearchService; +import io.reflectoring.resilience4j.springboot.services.failures.PotentialFailure; +import io.reflectoring.resilience4j.springboot.services.failures.PotentialFailureCheckedException; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class RetryingService { + + @Autowired + private FlightSearchService remoteSearchService; + + @Autowired + private RetryRegistry registry; + + @Retry(name = "basic") + public List basicExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @Retry(name = "throwingException") + public List searchFlightsThrowingException(SearchRequest request) throws Exception { + return remoteSearchService.searchFlightsThrowingException(request); + } + + @Retry(name = "predicateExample") + SearchResponse predicateExample(SearchRequest request) throws IOException { + return remoteSearchService.httpSearchFlights(request); + } + + + @Retry(name = "intervalFunctionRandomExample") + public List intervalFunctionRandom(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @Retry(name = "intervalFunctionExponentialExample") + public List intervalFunctionExponential(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @Retry(name = "asyncSearchExample") + public List asyncSearchExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + @Retry(name = "loggedRetryExample") + public List loggedRetryExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + public void setPotentialFailure(PotentialFailure potentialFailure) { + remoteSearchService.setPotentialFailure(potentialFailure); + } + + public void setPotentialFailureCheckedException( + PotentialFailureCheckedException potentialFailureCheckedException) { + remoteSearchService.setPotentialFailureCheckedException(potentialFailureCheckedException); + } + + @PostConstruct + public void postConstruct() { + registry + .retry("loggedRetryExample") + .getEventPublisher() + .onRetry(System.out::println); + } + + @Retry(name = "fallbackExample", fallbackMethod = "localCacheFlightSearch") + public List fallbackExample(SearchRequest request) { + return remoteSearchService.searchFlights(request); + } + + private List localCacheFlightSearch(SearchRequest request, RuntimeException re) { + System.out.println("Returning search results from cache"); + return Arrays.asList( + new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo())); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/SpringbootResilience4jApplication.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/SpringbootResilience4jApplication.java new file mode 100644 index 0000000..b0faf50 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/SpringbootResilience4jApplication.java @@ -0,0 +1,26 @@ +package io.reflectoring.resilience4j.springboot; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; + +@SpringBootApplication +public class SpringbootResilience4jApplication { + @Autowired + private RetryExamplesRunner retryExamplesRunner; + + @Autowired + private RateLimiterExamplesRunner rateLimiterExamplesRunner; + + public static void main(String[] args) { + SpringApplication.run(SpringbootResilience4jApplication.class, args); + } + + @EventListener(ApplicationReadyEvent.class) + public void runExamples() { + retryExamplesRunner.run(); + rateLimiterExamplesRunner.run(); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/FlightServiceBaseException.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/FlightServiceBaseException.java new file mode 100644 index 0000000..5aacd82 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/FlightServiceBaseException.java @@ -0,0 +1,7 @@ +package io.reflectoring.resilience4j.springboot.exceptions; + +public class FlightServiceBaseException extends RuntimeException { + public FlightServiceBaseException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/RateLimitExceededException.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/RateLimitExceededException.java new file mode 100644 index 0000000..e1fc580 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/RateLimitExceededException.java @@ -0,0 +1,14 @@ +package io.reflectoring.resilience4j.springboot.exceptions; + +public class RateLimitExceededException extends FlightServiceBaseException { + String errorCode; + + public RateLimitExceededException(String message, String errorCode) { + super(message); + this.errorCode = errorCode; + } + + public String getErrorCode() { + return errorCode; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/SeatsUnavailableException.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/SeatsUnavailableException.java new file mode 100644 index 0000000..dacf89c --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/exceptions/SeatsUnavailableException.java @@ -0,0 +1,7 @@ +package io.reflectoring.resilience4j.springboot.exceptions; + +public class SeatsUnavailableException extends FlightServiceBaseException { + public SeatsUnavailableException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/Flight.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/Flight.java new file mode 100644 index 0000000..808e92a --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/Flight.java @@ -0,0 +1,60 @@ +package io.reflectoring.resilience4j.springboot.model; + +public class Flight { + String flightNumber; + String flightDate; + String from; + String to; + + public Flight() { + } + + public Flight(String flightNumber, String flightDate, String from, String to) { + this.flightNumber = flightNumber; + this.flightDate = flightDate; + this.from = from; + this.to = to; + } + + public String getFlightNumber() { + return flightNumber; + } + + public void setFlightNumber(String flightNumber) { + this.flightNumber = flightNumber; + } + + public String getFlightDate() { + return flightDate; + } + + public void setFlightDate(String flightDate) { + this.flightDate = flightDate; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public String toString() { + return "Flight{" + + "flightNumber='" + flightNumber + '\'' + + ", flightDate='" + flightDate + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchRequest.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchRequest.java new file mode 100644 index 0000000..40610ec --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchRequest.java @@ -0,0 +1,25 @@ +package io.reflectoring.resilience4j.springboot.model; + +public class SearchRequest { + String from; + String to; + String flightDate; + + public SearchRequest(String from, String to, String flightDate) { + this.from = from; + this.to = to; + this.flightDate = flightDate; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public String getFlightDate() { + return flightDate; + } +} diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchResponse.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchResponse.java new file mode 100644 index 0000000..d7be8d6 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/model/SearchResponse.java @@ -0,0 +1,32 @@ +package io.reflectoring.resilience4j.springboot.model; + +import java.util.List; + +public class SearchResponse { + String errorCode; + List flights; + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public List getFlights() { + return flights; + } + + public void setFlights(List flights) { + this.flights = flights; + } + + @Override + public String toString() { + return "SearchResponse{" + + "errorCode='" + errorCode + '\'' + + ", flights=" + flights + + '}'; + } +} diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/predicates/ConditionalRetryPredicate.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/predicates/ConditionalRetryPredicate.java new file mode 100644 index 0000000..66ec101 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/predicates/ConditionalRetryPredicate.java @@ -0,0 +1,15 @@ +package io.reflectoring.resilience4j.springboot.predicates; + +import io.reflectoring.resilience4j.springboot.model.SearchResponse; +import java.util.function.Predicate; + +public class ConditionalRetryPredicate implements Predicate { + @Override + public boolean test(SearchResponse searchResponse) { + if (searchResponse.getErrorCode() != null) { + System.out.println("Search returned error code = " + searchResponse.getErrorCode()); + return searchResponse.getErrorCode().equals("FS-167"); + } + return false; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/FlightSearchService.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/FlightSearchService.java new file mode 100644 index 0000000..973ecc6 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/FlightSearchService.java @@ -0,0 +1,111 @@ +package io.reflectoring.resilience4j.springboot.services; + +import io.reflectoring.resilience4j.springboot.model.Flight; +import io.reflectoring.resilience4j.springboot.model.SearchRequest; +import io.reflectoring.resilience4j.springboot.model.SearchResponse; +import io.reflectoring.resilience4j.springboot.services.failures.NoCheckedExceptionFailure; +import io.reflectoring.resilience4j.springboot.services.failures.NoFailure; +import io.reflectoring.resilience4j.springboot.services.failures.PotentialFailure; +import io.reflectoring.resilience4j.springboot.services.failures.PotentialFailureCheckedException; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class FlightSearchService { + + PotentialFailure potentialFailure = new NoFailure(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss SSS"); + + PotentialFailureCheckedException potentialFailureCheckedException = new NoCheckedExceptionFailure(); + + public List searchFlights(SearchRequest request) { + System.out + .println("Searching for flights; current time = " + LocalDateTime.now().format(formatter)); + potentialFailure.occur(); + + List flights = Arrays.asList( + new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 732", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 746", request.getFlightDate(), request.getFrom(), request.getTo()) + ); + System.out.println("Flight search successful"); + return flights; + } + + public List searchFlightsThrowingException(SearchRequest request) throws Exception { + System.out.println("Searching for flights; current time = " + LocalDateTime.now().format(formatter)); + try { + if (!potentialFailureCheckedException.occur()) { + List flights = Arrays.asList( + new Flight("XY 765", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 781", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 732", request.getFlightDate(), request.getFrom(), request.getTo()), + new Flight("XY 746", request.getFlightDate(), request.getFrom(), request.getTo()) + ); + System.out.println("Flight search successful"); + return flights; + } + } catch (RuntimeException re) { + throw new Exception("Exception when searching for flights"); + } + return Collections.EMPTY_LIST; + } + + public void setPotentialFailure(PotentialFailure potentialFailure) { + this.potentialFailure = potentialFailure; + } + + public void setPotentialFailureCheckedException( + PotentialFailureCheckedException potentialFailureCheckedException) { + this.potentialFailureCheckedException = potentialFailureCheckedException; + } + + public SearchResponse httpSearchFlights(SearchRequest request) throws IOException { + System.out + .println("Searching for flights; current time = " + LocalDateTime.now().format(formatter)); + + String date = request.getFlightDate(); + String from = request.getFrom(); + String to = request.getTo(); + if (request.getFlightDate().equals("01/25/2021")) { // Simulating an error scenario + try { + if (!potentialFailure.occur()) { + List flights = Arrays.asList( + new Flight("XY 765", date, from, to), + new Flight("XY 781", date, from, to), + new Flight("XY 732", date, from, to), + new Flight("XY 746", date, from, to) + ); + System.out.println("Flight search successful"); + SearchResponse response = new SearchResponse(); + response.setFlights(flights); + return response; + } + } catch (RuntimeException re) { + System.out.println("Flight data initialization in progress, cannot search at this time"); + SearchResponse response = new SearchResponse(); + response.setErrorCode("FS-167"); + response.setFlights(Collections.emptyList()); + return response; + } + } + + potentialFailure.occur(); + List flights = Arrays.asList( + new Flight("XY 765", date, from, to), + new Flight("XY 781", date, from, to), + new Flight("XY 732", date, from, to), + new Flight("XY 746", date, from, to) + ); + System.out.println("Flight search successful"); + SearchResponse response = new SearchResponse(); + response.setFlights(flights); + return response; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailHalfTheTime.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailHalfTheTime.java new file mode 100644 index 0000000..b11a8d0 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailHalfTheTime.java @@ -0,0 +1,26 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +import java.util.Random; + +public class FailHalfTheTime implements PotentialFailure { + Random random = new Random(); + int times; + int failedCount; + + public FailHalfTheTime(int times) { + this.times = times; + } + + @Override + public boolean occur() { + if (failedCount++ < times && random.nextInt() % 2 == 0) { + throw new RuntimeException("Operation failed"); + } + return false; + } + + public static void main(String[] args) { + PotentialFailure failure = new FailHalfTheTime(4); + failure.occur(); + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimes.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimes.java new file mode 100644 index 0000000..63d3f6a --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimes.java @@ -0,0 +1,19 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public class FailNTimes implements PotentialFailure { + int times; + int failedCount; + + public FailNTimes(int times) { + this.times = times; + } + + @Override + public boolean occur() { + if (failedCount++ < times) { + System.out.println("Operation failed"); + throw new RuntimeException("Operation failed"); + } + return false; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimesCheckedException.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimesCheckedException.java new file mode 100644 index 0000000..8c55b67 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/FailNTimesCheckedException.java @@ -0,0 +1,19 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public class FailNTimesCheckedException implements PotentialFailureCheckedException { + int times; + int failedCount; + + public FailNTimesCheckedException(int times) { + this.times = times; + } + + @Override + public boolean occur() throws Exception { + if (failedCount++ < times) { + System.out.println("Operation failed, exception occurred"); + throw new Exception("Operation failed"); + } + return false; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoCheckedExceptionFailure.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoCheckedExceptionFailure.java new file mode 100644 index 0000000..8404d7e --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoCheckedExceptionFailure.java @@ -0,0 +1,8 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public class NoCheckedExceptionFailure implements PotentialFailureCheckedException { + @Override + public boolean occur() throws Exception { + return false; + } +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoFailure.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoFailure.java new file mode 100644 index 0000000..a5659d2 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/NoFailure.java @@ -0,0 +1,8 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public class NoFailure implements PotentialFailure { + @Override + public boolean occur() { + return false; + } +} diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailure.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailure.java new file mode 100644 index 0000000..df6a177 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailure.java @@ -0,0 +1,5 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public interface PotentialFailure { + boolean occur(); +} \ No newline at end of file diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailureCheckedException.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailureCheckedException.java new file mode 100644 index 0000000..e33b67c --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/PotentialFailureCheckedException.java @@ -0,0 +1,6 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +public interface PotentialFailureCheckedException { + boolean occur() throws Exception; +} + diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/RateLimitFailNTimes.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/RateLimitFailNTimes.java new file mode 100644 index 0000000..59021f8 --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/RateLimitFailNTimes.java @@ -0,0 +1,21 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +import io.reflectoring.resilience4j.springboot.exceptions.RateLimitExceededException; + +public class RateLimitFailNTimes implements PotentialFailure { + int times; + int failedCount; + + public RateLimitFailNTimes(int times) { + this.times = times; + } + + @Override + public boolean occur() { + if (failedCount++ < times) { + System.out.println("Rate limit exceeded"); + throw new RateLimitExceededException("Rate limit exceeded, try again in some time", "RL-101"); + } + return false; + } +} diff --git a/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/SeatsUnavailableFailureNTimes.java b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/SeatsUnavailableFailureNTimes.java new file mode 100644 index 0000000..519ce8d --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/java/io/reflectoring/resilience4j/springboot/services/failures/SeatsUnavailableFailureNTimes.java @@ -0,0 +1,21 @@ +package io.reflectoring.resilience4j.springboot.services.failures; + +import io.reflectoring.resilience4j.springboot.exceptions.SeatsUnavailableException; + +public class SeatsUnavailableFailureNTimes implements PotentialFailure { + int times; + int failedCount; + + public SeatsUnavailableFailureNTimes(int times) { + this.times = times; + } + + @Override + public boolean occur() { + if (failedCount++ < times) { + System.out.println("Seats not available"); + throw new SeatsUnavailableException("Seats not available"); + } + return false; + } +} diff --git a/resilience4j/springboot-resilience4j/src/main/resources/application.yml b/resilience4j/springboot-resilience4j/src/main/resources/application.yml new file mode 100644 index 0000000..729a76f --- /dev/null +++ b/resilience4j/springboot-resilience4j/src/main/resources/application.yml @@ -0,0 +1,106 @@ +resilience4j: + retry: + instances: + + # Retry object used in RetryingService.basicExample() + basic: + maxRetryAttempts: 3 + waitDuration: 2s + + # Retry object used in RetryingService.fallbackExample() + fallbackExample: + maxRetryAttempts: 3 + waitDuration: 2s + + # Retry object used in RetryingService.intervalFunctionExponentialExample() + intervalFunctionExponentialExample: + enableExponentialBackoff: true + exponentialBackoffMultiplier: 2 + maxRetryAttempts: 6 + waitDuration: 1s + + # Retry object used in RetryingService.intervalFunctionRandomExample() + intervalFunctionRandomExample: + enableRandomizedWait: true + maxRetryAttempts: 3 + randomizedWaitFactor: 0.5 + waitDuration: 2s + + # Retry object used in RetryingService.loggedRetryExample() + loggedRetryExample: + maxRetryAttempts: 3 + waitDuration: 2s + + # Retry object used in RetryingService.predicateExample() + predicateExample: + maxRetryAttempts: 3 + resultPredicate: io.reflectoring.resilience4j.springboot.predicates.ConditionalRetryPredicate + waitDuration: 3s + + # Retry object used in RetryingService.basicExample_serviceThrowingException() + throwingException: + maxRetryAttempts: 3 + retryExceptions: + - java.lang.Exception + waitDuration: 2s + + # Retry object used in RateLimitingService.retryAndRateLimitExample() + retryAndRateLimitExample: + maxRetryAttempts: 2 + waitDuration: 1s + + ratelimiter: + instances: + + # RateLimiter object used in RateLimitingService.basicExample() + basicExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 1s + + # RateLimiter object used in RateLimitingService.timeoutExample() + timeoutExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 250ms + + # RateLimiter object used in RateLimitingService.multipleRateLimitsExample() + multipleRateLimiters_rps_limiter: + limitForPeriod: 2 + limitRefreshPeriod: 1s + timeoutDuration: 2s + + multipleRateLimiters_rpm_limiter: + limitForPeriod: 40 + limitRefreshPeriod: 1m + timeoutDuration: 2s + + # RateLimiter object used in RateLimitingService.changeLimitsExample() + changeLimitsExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 1s + + # RateLimiter object used in RateLimitingService.retryAndRateLimitExample() + retryAndRateLimitExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 250ms + + # RateLimiter object used in RateLimitingService.rateLimiterEventsExample() + rateLimiterEventsExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 50ms + + # RateLimiter object used in RateLimitingService.fallbackExample() + fallbackExample: + limitForPeriod: 1 + limitRefreshPeriod: 1s + timeoutDuration: 500ms + +management: + endpoints: + web: + exposure: + include: '*' \ No newline at end of file diff --git a/spring-boot/feature-flags/.gitignore b/spring-boot/feature-flags/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/spring-boot/feature-flags/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-boot/feature-flags/.mvn/wrapper/MavenWrapperDownloader.java b/spring-boot/feature-flags/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..b901097 --- /dev/null +++ b/spring-boot/feature-flags/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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 + * + * 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.jar b/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.properties b/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/spring-boot/feature-flags/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-boot/feature-flags/mvnw b/spring-boot/feature-flags/mvnw new file mode 100755 index 0000000..41c0f0c --- /dev/null +++ b/spring-boot/feature-flags/mvnw @@ -0,0 +1,310 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-boot/feature-flags/mvnw.cmd b/spring-boot/feature-flags/mvnw.cmd new file mode 100644 index 0000000..8611571 --- /dev/null +++ b/spring-boot/feature-flags/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-boot/feature-flags/pom.xml b/spring-boot/feature-flags/pom.xml new file mode 100644 index 0000000..7c1bf86 --- /dev/null +++ b/spring-boot/feature-flags/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.1 + + + io.reflectoring + feature-flags + 0.0.1-SNAPSHOT + feature-flags + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.togglz + togglz-spring-boot-starter + 2.6.1.Final + + + org.togglz + togglz-console + 2.6.1.Final + + + + + com.launchdarkly + launchdarkly-java-server-sdk + 5.3.0 + + + + + org.ff4j + ff4j-spring-boot-starter + 1.8.11 + + + org.ff4j + ff4j-web + 1.8.11 + + + org.ff4j + ff4j-config-yaml + 1.8.11 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + true + + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 + + + + + + + diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagService.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagService.java new file mode 100644 index 0000000..50157a4 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagService.java @@ -0,0 +1,25 @@ +package io.reflectoring.featureflags; + +public interface FeatureFlagService { + + /** + * A simple Boolean feature flag that returns either true or false for all users. + */ + Boolean isGlobalBooleanFeatureActive(); + + /** + * A Boolean feature flag that is targeted at a cohort of users. + */ + Boolean isPercentageRolloutActive(); + + /** + * A number-based feature flag that returns a number controlled by the Feature Flag service. + */ + Integer getNumberFlag(); + + /** + * A feature flag based on a previous user action. + */ + Boolean isUserActionTargetedFeatureActive(); + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagsApplication.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagsApplication.java new file mode 100644 index 0000000..eaa5d92 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/FeatureFlagsApplication.java @@ -0,0 +1,13 @@ +package io.reflectoring.featureflags; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FeatureFlagsApplication { + + public static void main(String[] args) { + SpringApplication.run(FeatureFlagsApplication.class, args); + } + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/UserContext.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/UserContext.java new file mode 100644 index 0000000..15ee301 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/UserContext.java @@ -0,0 +1,4 @@ +package io.reflectoring.featureflags; + +public class UserContext { +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JConfiguration.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JConfiguration.java new file mode 100644 index 0000000..b1a9344 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JConfiguration.java @@ -0,0 +1,15 @@ +package io.reflectoring.featureflags.ff4j; + +import org.ff4j.FF4j; +import org.ff4j.conf.XmlParser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FF4JConfiguration { + + @Bean("ff4jConfig") + public FF4j ff4J(){ + return new FF4j(new XmlParser(), "ff4j.xml"); + } +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JFeatureFlagService.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JFeatureFlagService.java new file mode 100644 index 0000000..023c1d7 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/FF4JFeatureFlagService.java @@ -0,0 +1,36 @@ +package io.reflectoring.featureflags.ff4j; + +import io.reflectoring.featureflags.FeatureFlagService; +import org.ff4j.FF4j; +import org.springframework.stereotype.Component; + +@Component("ff4j") +public class FF4JFeatureFlagService implements FeatureFlagService { + + private final FF4j ff4j; + + public FF4JFeatureFlagService(FF4j ff4j) { + this.ff4j = ff4j; + } + + @Override + public Boolean isGlobalBooleanFeatureActive() { + return ff4j.check("global-boolean-flag"); + } + + @Override + public Boolean isPercentageRolloutActive() { + return ff4j.check("global-percentage-rollout"); + } + + @Override + public Integer getNumberFlag() { + return null; + } + + @Override + public Boolean isUserActionTargetedFeatureActive() { + return null; + } + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/UserClickedFlippingStrategy.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/UserClickedFlippingStrategy.java new file mode 100644 index 0000000..7524cf3 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/ff4j/UserClickedFlippingStrategy.java @@ -0,0 +1,25 @@ +package io.reflectoring.featureflags.ff4j; + +import org.ff4j.core.FeatureStore; +import org.ff4j.core.FlippingExecutionContext; +import org.ff4j.core.FlippingStrategy; + +import java.util.Map; + +public class UserClickedFlippingStrategy implements FlippingStrategy { + + @Override + public void init(String featureName, Map initParam) { + + } + + @Override + public Map getInitParams() { + return null; + } + + @Override + public boolean evaluate(String featureName, FeatureStore store, FlippingExecutionContext executionContext) { + return false; + } +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyConfiguration.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyConfiguration.java new file mode 100644 index 0000000..1b80fe2 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyConfiguration.java @@ -0,0 +1,27 @@ +package io.reflectoring.featureflags.launchdarkly; + +import com.launchdarkly.sdk.server.LDClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PreDestroy; +import java.io.IOException; + +@Configuration +public class LaunchDarklyConfiguration { + + private LDClient launchdarklyClient; + + @Bean + public LDClient launchdarklyClient(@Value("${launchdarkly.sdkKey}") String sdkKey) { + this.launchdarklyClient = new LDClient(sdkKey); + return this.launchdarklyClient; + } + + @PreDestroy + public void destroy() throws IOException { + this.launchdarklyClient.close(); + } + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyFeatureFlagService.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyFeatureFlagService.java new file mode 100644 index 0000000..e956c80 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/launchdarkly/LaunchDarklyFeatureFlagService.java @@ -0,0 +1,64 @@ +package io.reflectoring.featureflags.launchdarkly; + +import com.launchdarkly.sdk.LDUser; +import com.launchdarkly.sdk.server.LDClient; +import io.reflectoring.featureflags.FeatureFlagService; +import io.reflectoring.featureflags.web.UserSession; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component("launchdarkly") +public class LaunchDarklyFeatureFlagService implements FeatureFlagService { + + private final LDClient launchdarklyClient; + private final UserSession userSession; + + public LaunchDarklyFeatureFlagService(LDClient launchdarklyClient, UserSession userSession) { + this.launchdarklyClient = launchdarklyClient; + this.userSession = userSession; + } + + /** + * Initializing LaunchDarkly users so that they are in the expected state at the start of the app. + * This is for testing purposes only. + */ + @PostConstruct + public void initUsers() { + launchdarklyClient.identify(new LDUser.Builder("alice") + .custom("clicked", true) + .build()); + launchdarklyClient.identify(new LDUser.Builder("bob") + .custom("clicked", true) + .build()); + launchdarklyClient.identify(new LDUser.Builder("charlie") + .custom("clicked", true) + .build()); + } + + @Override + public Boolean isGlobalBooleanFeatureActive() { + return launchdarklyClient.boolVariation("global-boolean-flag", getLaunchdarklyUserFromSession(), false); + } + + @Override + public Boolean isPercentageRolloutActive() { + return launchdarklyClient.boolVariation("user-based-percentage-rollout", getLaunchdarklyUserFromSession(), false); + } + + @Override + public Integer getNumberFlag() { + return null; + } + + @Override + public Boolean isUserActionTargetedFeatureActive() { + return launchdarklyClient.boolVariation("user-clicked-flag", getLaunchdarklyUserFromSession(), false); + } + + private LDUser getLaunchdarklyUserFromSession() { + return new LDUser.Builder(userSession.getUsername()) + .custom("clicked", userSession.hasClicked()) + .build(); + } +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/Features.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/Features.java new file mode 100644 index 0000000..4eb386d --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/Features.java @@ -0,0 +1,27 @@ +package io.reflectoring.featureflags.togglz; + +import org.togglz.core.Feature; +import org.togglz.core.activation.GradualActivationStrategy; +import org.togglz.core.annotation.ActivationParameter; +import org.togglz.core.annotation.DefaultActivationStrategy; +import org.togglz.core.annotation.EnabledByDefault; +import org.togglz.core.context.FeatureContext; + +public enum Features implements Feature { + + GLOBAL_BOOLEAN_FLAG, + + @EnabledByDefault + @DefaultActivationStrategy(id = GradualActivationStrategy.ID, parameters = { + @ActivationParameter(name = GradualActivationStrategy.PARAM_PERCENTAGE, value = "50") + }) + USER_BASED_PERCENTAGE_ROLLOUT, + + @EnabledByDefault + @DefaultActivationStrategy(id = "clicked") + USER_ACTION_TARGETED_FEATURE; + + public boolean isActive() { + return FeatureContext.getFeatureManager().isActive(this); + } +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TogglzUserProvider.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TogglzUserProvider.java new file mode 100644 index 0000000..f18089d --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TogglzUserProvider.java @@ -0,0 +1,40 @@ +package io.reflectoring.featureflags.togglz; + +import io.reflectoring.featureflags.web.UserSession; +import org.springframework.stereotype.Component; +import org.togglz.core.user.FeatureUser; +import org.togglz.core.user.UserProvider; + +@Component +public class TogglzUserProvider implements UserProvider { + + private final UserSession userSession; + + public TogglzUserProvider(UserSession userSession) { + this.userSession = userSession; + } + + @Override + public FeatureUser getCurrentUser() { + return new FeatureUser() { + @Override + public String getName() { + return userSession.getUsername(); + } + + @Override + public boolean isFeatureAdmin() { + return false; + } + + @Override + public Object getAttribute(String attributeName) { + if (attributeName.equals("clicked")) { + return userSession.hasClicked(); + } + return null; + } + }; + } + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TooglzFeatureFlagService.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TooglzFeatureFlagService.java new file mode 100644 index 0000000..15e6b98 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/TooglzFeatureFlagService.java @@ -0,0 +1,29 @@ +package io.reflectoring.featureflags.togglz; + +import io.reflectoring.featureflags.FeatureFlagService; +import org.springframework.stereotype.Component; + +@Component("togglz") +public class TooglzFeatureFlagService implements FeatureFlagService { + + @Override + public Boolean isGlobalBooleanFeatureActive() { + return Features.GLOBAL_BOOLEAN_FLAG.isActive(); + } + + @Override + public Boolean isPercentageRolloutActive() { + return Features.USER_BASED_PERCENTAGE_ROLLOUT.isActive(); + } + + @Override + public Integer getNumberFlag() { + return null; + } + + @Override + public Boolean isUserActionTargetedFeatureActive() { + return Features.USER_ACTION_TARGETED_FEATURE.isActive(); + } + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/UserClickedActivationStrategy.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/UserClickedActivationStrategy.java new file mode 100644 index 0000000..df761f2 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/togglz/UserClickedActivationStrategy.java @@ -0,0 +1,29 @@ +package io.reflectoring.featureflags.togglz; + +import org.togglz.core.activation.Parameter; +import org.togglz.core.repository.FeatureState; +import org.togglz.core.spi.ActivationStrategy; +import org.togglz.core.user.FeatureUser; + +public class UserClickedActivationStrategy implements ActivationStrategy { + + @Override + public String getId() { + return "clicked"; + } + + @Override + public String getName() { + return "Rollout based on user click"; + } + + @Override + public boolean isActive(FeatureState featureState, FeatureUser user) { + return (Boolean) user.getAttribute("clicked"); + } + + @Override + public Parameter[] getParameters() { + return new Parameter[0]; + } +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/UserSession.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/UserSession.java new file mode 100644 index 0000000..4762bdd --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/UserSession.java @@ -0,0 +1,49 @@ +package io.reflectoring.featureflags.web; + +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Component; +import org.springframework.web.context.WebApplicationContext; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +@Component("userSession") +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) +public class UserSession implements Serializable { + + private static ThreadLocal threadLocalUserSession; + + public static void setThreadLocalSession(UserSession userSession){ + threadLocalUserSession.set(userSession); + } + + public static UserSession get(){ + return threadLocalUserSession.get(); + } + + private Map userClicked = new HashMap<>(); + private String username; + + public void login(String username){ + this.username = username; + } + + public String getUsername(){ + return this.username; + } + + public boolean hasClicked(){ + if(!userClicked.containsKey(this.username)){ + return false; + } + return userClicked.get(username); + } + + public void recordClick(){ + userClicked.put(this.username, Boolean.TRUE); + } + + +} diff --git a/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/WebController.java b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/WebController.java new file mode 100644 index 0000000..729fbc4 --- /dev/null +++ b/spring-boot/feature-flags/src/main/java/io/reflectoring/featureflags/web/WebController.java @@ -0,0 +1,46 @@ +package io.reflectoring.featureflags.web; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +class WebController { + + private final UserSession userSession; + + WebController(UserSession userSession) { + this.userSession = userSession; + } + + @GetMapping(path = {"/"}) + ModelAndView chooseUser() { + return new ModelAndView("/choose-user.html"); + } + + @GetMapping(path = {"/alice"}) + ModelAndView alice() { + userSession.login("alice"); + return new ModelAndView("/features.html"); + } + + @GetMapping(path = {"/bob"}) + ModelAndView bob() { + userSession.login("bob"); + return new ModelAndView("/features.html"); + } + + @GetMapping(path = {"/charlie"}) + ModelAndView charlie() { + userSession.login("charlie"); + return new ModelAndView("/features.html"); + } + + @PostMapping(path = {"/click"}) + ModelAndView click() { + userSession.recordClick(); + return new ModelAndView("/features.html"); + } + +} diff --git a/spring-boot/feature-flags/src/main/resources/META-INF/services/org.togglz.core.spi.ActivationStrategy b/spring-boot/feature-flags/src/main/resources/META-INF/services/org.togglz.core.spi.ActivationStrategy new file mode 100644 index 0000000..e07f1f0 --- /dev/null +++ b/spring-boot/feature-flags/src/main/resources/META-INF/services/org.togglz.core.spi.ActivationStrategy @@ -0,0 +1 @@ +io.reflectoring.featureflags.togglz.UserClickedActivationStrategy \ No newline at end of file diff --git a/spring-boot/feature-flags/src/main/resources/application.yml b/spring-boot/feature-flags/src/main/resources/application.yml new file mode 100644 index 0000000..091bb99 --- /dev/null +++ b/spring-boot/feature-flags/src/main/resources/application.yml @@ -0,0 +1,18 @@ +launchdarkly: + sdkKey: + +logging: + level: + org.togglz: DEBUG + com.launchdarkly: TRACE + +togglz: + feature-enums: io.reflectoring.featureflags.togglz.Features + features: + GLOBAL_BOOLEAN_FLAG: + enabled: true + console: + enabled: true + secured: false + path: /togglz + use-management-port: false \ No newline at end of file diff --git a/spring-boot/feature-flags/src/main/resources/ff4j.xml b/spring-boot/feature-flags/src/main/resources/ff4j.xml new file mode 100644 index 0000000..9599d48 --- /dev/null +++ b/spring-boot/feature-flags/src/main/resources/ff4j.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-boot/feature-flags/src/main/resources/templates/choose-user.html b/spring-boot/feature-flags/src/main/resources/templates/choose-user.html new file mode 100644 index 0000000..db8c2fc --- /dev/null +++ b/spring-boot/feature-flags/src/main/resources/templates/choose-user.html @@ -0,0 +1,20 @@ + + + + + + + Who are you? + + + + +

Who are you?

+ + + + diff --git a/spring-boot/feature-flags/src/main/resources/templates/features.html b/spring-boot/feature-flags/src/main/resources/templates/features.html new file mode 100644 index 0000000..773cf86 --- /dev/null +++ b/spring-boot/feature-flags/src/main/resources/templates/features.html @@ -0,0 +1,61 @@ + + + + + + + Your features + + + + + + +

Who are you?

+ + +

Bob's features

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TogglzFF4JLaunchdarkly
global boolean feature
user-based percentage rollout (50%)
rollout based on user clickN/A
+ +
+ +
+ +
+ +
+ + diff --git a/spring-boot/feature-flags/src/test/java/io/reflectoring/featureflags/FeatureFlagsApplicationTests.java b/spring-boot/feature-flags/src/test/java/io/reflectoring/featureflags/FeatureFlagsApplicationTests.java new file mode 100644 index 0000000..661ff29 --- /dev/null +++ b/spring-boot/feature-flags/src/test/java/io/reflectoring/featureflags/FeatureFlagsApplicationTests.java @@ -0,0 +1,13 @@ +package io.reflectoring.featureflags; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class FeatureFlagsApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-boot/zero-downtime/.gitignore b/spring-boot/zero-downtime/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/spring-boot/zero-downtime/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-boot/zero-downtime/.mvn/wrapper/MavenWrapperDownloader.java b/spring-boot/zero-downtime/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..e76d1f3 --- /dev/null +++ b/spring-boot/zero-downtime/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or 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. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.jar b/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..2cc7d4a Binary files /dev/null and b/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.properties b/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffdc10e --- /dev/null +++ b/spring-boot/zero-downtime/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/spring-boot/zero-downtime/mvnw b/spring-boot/zero-downtime/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/spring-boot/zero-downtime/mvnw @@ -0,0 +1,310 @@ +#!/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 +# +# 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-boot/zero-downtime/mvnw.cmd b/spring-boot/zero-downtime/mvnw.cmd new file mode 100644 index 0000000..82abbbb --- /dev/null +++ b/spring-boot/zero-downtime/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. 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, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a customer defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-boot/zero-downtime/pom.xml b/spring-boot/zero-downtime/pom.xml new file mode 100644 index 0000000..6c28e97 --- /dev/null +++ b/spring-boot/zero-downtime/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.2 + + + io.reflectoring + zero-downtime + 0.0.1-SNAPSHOT + zero-downtime + Demo project to showcase zero downtime deployment + + 11 + + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.flywaydb + flyway-core + + + + com.h2database + h2 + + + + + com.launchdarkly + launchdarkly-java-server-sdk + 5.3.0 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerController.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerController.java new file mode 100644 index 0000000..95ad61f --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerController.java @@ -0,0 +1,64 @@ +package io.reflectoring.zerodowntime; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Optional; + +@RestController +public class CustomerController { + + private final CustomerRepository oldCustomerRepository; + private final NewCustomerRepository newCustomerRepository; + private final FeatureFlagService featureFlagService; + + public CustomerController(CustomerRepository oldCustomerRepository, NewCustomerRepository newCustomerRepository, FeatureFlagService featureFlagService) { + this.oldCustomerRepository = oldCustomerRepository; + this.newCustomerRepository = newCustomerRepository; + this.featureFlagService = featureFlagService; + } + + @GetMapping("/customers/create") + String createCustomer() { + if (featureFlagService.writeToNewCustomerSchema()) { + NewCustomer customer = new NewCustomer("Bob", "Builder", "Build Street", "21"); + newCustomerRepository.save(customer); + } else { + OldCustomer customer = new OldCustomer("Bob", "Builder", "21 Build Street"); + oldCustomerRepository.save(customer); + } + return "customer created"; + } + + @GetMapping("/customers/{id}}") + String getCustomer(@PathVariable("id") Long id) { + if (featureFlagService.readFromNewCustomerSchema()) { + Optional customer = newCustomerRepository.findById(id); + return customer.get().toString(); + } else { + Optional customer = oldCustomerRepository.findById(id); + return customer.get().toString(); + } + } + + @GetMapping("/customers/list") + String getCustomer() { + StringBuffer buffer = new StringBuffer(); + if (featureFlagService.readFromNewCustomerSchema()) { + Iterable customers = newCustomerRepository.findAll(); + for (NewCustomer customer : customers) { + buffer.append("\n"); + buffer.append(customer.toString()); + } + } else { + Iterable customers = oldCustomerRepository.findAll(); + for (OldCustomer customer : customers) { + buffer.append("\n"); + buffer.append(customer.toString()); + } + } + return buffer.toString(); + } + +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerRepository.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerRepository.java new file mode 100644 index 0000000..8302e94 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/CustomerRepository.java @@ -0,0 +1,6 @@ +package io.reflectoring.zerodowntime; + +import org.springframework.data.repository.CrudRepository; + +public interface CustomerRepository extends CrudRepository { +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FeatureFlagService.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FeatureFlagService.java new file mode 100644 index 0000000..35837dc --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FeatureFlagService.java @@ -0,0 +1,24 @@ +package io.reflectoring.zerodowntime; + +import com.launchdarkly.sdk.LDUser; +import com.launchdarkly.sdk.server.LDClient; +import org.springframework.stereotype.Component; + +@Component +public class FeatureFlagService { + + private final LDClient launchdarklyClient; + + public FeatureFlagService(LDClient launchdarklyClient) { + this.launchdarklyClient = launchdarklyClient; + } + + public Boolean writeToNewCustomerSchema() { + return true; + } + + public Boolean readFromNewCustomerSchema() { + return false; + } + +} \ No newline at end of file diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayConfiguration.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayConfiguration.java new file mode 100644 index 0000000..8f03865 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayConfiguration.java @@ -0,0 +1,19 @@ +package io.reflectoring.zerodowntime; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +class FlywayConfiguration { + + private final static Logger logger = LoggerFactory.getLogger(FlywayConfiguration.class); + + @Bean + FlywayMigrationStrategy flywayStrategy() { + return flyway -> logger.info("Flyway migration on startup is disabled! Call the endpoint /flywayMigrate instead."); + } + +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayController.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayController.java new file mode 100644 index 0000000..da0407b --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/FlywayController.java @@ -0,0 +1,22 @@ +package io.reflectoring.zerodowntime; + +import org.flywaydb.core.Flyway; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +class FlywayController { + + private final Flyway flyway; + + public FlywayController(Flyway flyway) { + this.flyway = flyway; + } + + @GetMapping("/flywayMigrate") + String flywayMigrate() { + flyway.migrate(); + return "success"; + } + +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/LaunchDarklyConfiguration.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/LaunchDarklyConfiguration.java new file mode 100644 index 0000000..7f7b82a --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/LaunchDarklyConfiguration.java @@ -0,0 +1,27 @@ +package io.reflectoring.zerodowntime; + +import com.launchdarkly.sdk.server.LDClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PreDestroy; +import java.io.IOException; + +@Configuration +public class LaunchDarklyConfiguration { + + private LDClient launchdarklyClient; + + @Bean + public LDClient launchdarklyClient(@Value("${launchdarkly.sdkKey}") String sdkKey) { + this.launchdarklyClient = new LDClient(sdkKey); + return this.launchdarklyClient; + } + + @PreDestroy + public void destroy() throws IOException { + this.launchdarklyClient.close(); + } + +} \ No newline at end of file diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomer.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomer.java new file mode 100644 index 0000000..b8a5f0c --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomer.java @@ -0,0 +1,34 @@ +package io.reflectoring.zerodowntime; + + +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +@Table("CUSTOMER") +public class NewCustomer { + + @Id + private long id; + private String firstName; + private String lastName; + private String addressStreet; + private String addressStreetNumber; + + public NewCustomer(String firstName, String lastName, String addressStreet, String addressStreetNumber) { + this.firstName = firstName; + this.lastName = lastName; + this.addressStreet = addressStreet; + this.addressStreetNumber = addressStreetNumber; + } + + @Override + public String toString() { + return "NewCustomer{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", addressStreet='" + addressStreet + '\'' + + ", addressStreetNumber='" + addressStreetNumber + '\'' + + '}'; + } +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomerRepository.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomerRepository.java new file mode 100644 index 0000000..70ad017 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/NewCustomerRepository.java @@ -0,0 +1,6 @@ +package io.reflectoring.zerodowntime; + +import org.springframework.data.repository.CrudRepository; + +public interface NewCustomerRepository extends CrudRepository { +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/OldCustomer.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/OldCustomer.java new file mode 100644 index 0000000..f9f5008 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/OldCustomer.java @@ -0,0 +1,31 @@ +package io.reflectoring.zerodowntime; + + +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Table; + +@Table("CUSTOMER") +public class OldCustomer { + + @Id + private long id; + private String firstName; + private String lastName; + private String address; + + public OldCustomer(String firstName, String lastName, String address) { + this.firstName = firstName; + this.lastName = lastName; + this.address = address; + } + + @Override + public String toString() { + return "OldCustomer{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", address='" + address + '\'' + + '}'; + } +} diff --git a/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/ZeroDowntimeApplication.java b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/ZeroDowntimeApplication.java new file mode 100644 index 0000000..cd49a3e --- /dev/null +++ b/spring-boot/zero-downtime/src/main/java/io/reflectoring/zerodowntime/ZeroDowntimeApplication.java @@ -0,0 +1,17 @@ +package io.reflectoring.zerodowntime; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@SpringBootApplication +@EnableJdbcRepositories +@EnableWebMvc +public class ZeroDowntimeApplication { + + public static void main(String[] args) { + SpringApplication.run(ZeroDowntimeApplication.class, args); + } + +} diff --git a/spring-boot/zero-downtime/src/main/resources/application.yml b/spring-boot/zero-downtime/src/main/resources/application.yml new file mode 100644 index 0000000..f31ca52 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/resources/application.yml @@ -0,0 +1,12 @@ +spring.datasource.url: "jdbc:h2:file:~/testdb" +spring.datasource.driverClassName: org.h2.Driver +spring.datasource.username: +spring.datasource.password: +spring.jpa.database-platform: org.hibernate.dialect.H2Dialect +spring: + h2: + console: + enabled: true + +launchdarkly: + sdkKey: \ No newline at end of file diff --git a/spring-boot/zero-downtime/src/main/resources/db/migration/V001__create_table_user.sql b/spring-boot/zero-downtime/src/main/resources/db/migration/V001__create_table_user.sql new file mode 100644 index 0000000..d781777 --- /dev/null +++ b/spring-boot/zero-downtime/src/main/resources/db/migration/V001__create_table_user.sql @@ -0,0 +1,7 @@ +create table customer ( + id serial unique, + first_name varchar(100), + last_name varchar(100), + address varchar(255), + primary key (id) +); diff --git a/spring-boot/zero-downtime/src/main/resources/db/migration/V002__add_address_columns.sql b/spring-boot/zero-downtime/src/main/resources/db/migration/V002__add_address_columns.sql new file mode 100644 index 0000000..eb5595f --- /dev/null +++ b/spring-boot/zero-downtime/src/main/resources/db/migration/V002__add_address_columns.sql @@ -0,0 +1,2 @@ +alter table customer add column address_street varchar(100) null; +alter table customer add column address_street_number varchar(100) null; diff --git a/spring-boot/zero-downtime/src/test/java/io/reflectoring/zerodowntime/ZeroDowntimeApplicationTests.java b/spring-boot/zero-downtime/src/test/java/io/reflectoring/zerodowntime/ZeroDowntimeApplicationTests.java new file mode 100644 index 0000000..7efba2e --- /dev/null +++ b/spring-boot/zero-downtime/src/test/java/io/reflectoring/zerodowntime/ZeroDowntimeApplicationTests.java @@ -0,0 +1,13 @@ +package io.reflectoring.zerodowntime; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ZeroDowntimeApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-data/spring-data-rest-springfox/build.gradle b/spring-data/spring-data-rest-springfox/build.gradle index 7b098b8..02ed173 100644 --- a/spring-data/spring-data-rest-springfox/build.gradle +++ b/spring-data/spring-data-rest-springfox/build.gradle @@ -27,9 +27,9 @@ repositories { dependencies { compile('org.springframework.boot:spring-boot-starter-data-jpa') compile('org.springframework.boot:spring-boot-starter-data-rest') - compile('io.springfox:springfox-data-rest:2.7.1-SNAPSHOT') - compile('io.springfox:springfox-swagger2:2.7.1-SNAPSHOT') - compile('io.springfox:springfox-swagger-ui:2.7.1-SNAPSHOT') + compile('io.springfox:springfox-data-rest:2.8.0') + compile('io.springfox:springfox-swagger2:2.8.0') + compile('io.springfox:springfox-swagger-ui:2.8.0') compile('com.h2database:h2:1.4.196') testCompile('org.springframework.boot:spring-boot-starter-test') }