Merge branch 'master' into master

This commit is contained in:
johnA1331
2020-06-14 10:42:24 +08:00
committed by GitHub
420 changed files with 7709 additions and 1293 deletions
+1
View File
@@ -15,6 +15,7 @@
<modules>
<module>spring-boot</module>
<module>spring-boot-1</module>
<module>spring-boot-admin</module>
<module>spring-boot-angular</module>
<module>spring-boot-annotations</module>
@@ -0,0 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
@@ -0,0 +1,6 @@
## Spring Boot 1.x Actuator
This module contains articles about Spring Boot Actuator in Spring Boot version 1.x.
## Relevant articles:
- [Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators)
+234
View File
@@ -0,0 +1,234 @@
#!/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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 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 Migwn, 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
))"
# TODO classpath?
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
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
echo $MAVEN_PROJECTBASEDIR
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
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 "$@"
+143
View File
@@ -0,0 +1,143 @@
@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 Maven2 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 key stroke 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 enable echoing my 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
%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%
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-1</artifactId>
<packaging>jar</packaging>
<description>Module for Spring Boot version 1.x</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-1</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,35 @@
package com.baeldung.actuator;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class CustomEndpoint implements Endpoint<List<String>> {
@Override
public String getId() {
return "customEndpoint";
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isSensitive() {
return true;
}
@Override
public List<String> invoke() {
// Custom logic to build the output
List<String> messages = new ArrayList<>();
messages.add("This is message 1");
messages.add("This is message 2");
return messages;
}
}
@@ -0,0 +1,23 @@
package com.baeldung.actuator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component("myHealthCheck")
public class HealthCheck implements HealthIndicator {
@Override
public Health health() {
int errorCode = check(); // perform some specific health check
if (errorCode != 0) {
return Health.down().withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
public int check() {
// Our logic to check health
return 0;
}
}
@@ -0,0 +1,28 @@
package com.baeldung.actuator;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class LoginServiceImpl {
private final CounterService counterService;
public LoginServiceImpl(CounterService counterService) {
this.counterService = counterService;
}
public boolean login(String userName, char[] password) {
boolean success;
if (userName.equals("admin") && Arrays.equals("secret".toCharArray(), password)) {
counterService.increment("counter.login.success");
success = true;
} else {
counterService.increment("counter.login.failure");
success = false;
}
return success;
}
}
@@ -0,0 +1,13 @@
package com.baeldung.actuator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoot {
public static void main(String[] args) {
SpringApplication.run(SpringBoot.class, args);
}
}
@@ -0,0 +1,19 @@
### server port
server.port=8080
#port used to expose actuator
management.port=8081
#CIDR allowed to hit actuator
management.address=127.0.0.1
# Actuator Configuration
# customize /beans endpoint
endpoints.beans.id=springbeans
endpoints.beans.sensitive=false
endpoints.beans.enabled=true
# for the Spring Boot version 1.5.0 and above, we have to disable security to expose the health endpoint fully for unauthorized access.
# see: https://docs.spring.io/spring-boot/docs/1.5.x/reference/html/production-ready-monitoring.html
management.security.enabled=false
endpoints.health.sensitive=false
# customize /info endpoint
info.app.name=Spring Sample Application
info.app.description=This is my first spring boot application
info.app.version=1.0.0
@@ -0,0 +1,46 @@
package com.baeldung.actuator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
public class CustomEndpointIntegrationTest {
@LocalManagementPort
private int port;
private RestTemplate restTemplate = new RestTemplate();
@Autowired
private ObjectMapper objectMapper;
@Test
public void whenSpringContextIsBootstrapped_thenActuatorCustomEndpointWorks() throws IOException {
ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:" + port + "/customEndpoint", String.class);
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
List<String> response = objectMapper.readValue(entity.getBody(), new TypeReference<ArrayList<String>>() {
});
assertThat(response, hasItems("This is message 1", "This is message 2"));
}
}
@@ -0,0 +1,49 @@
package com.baeldung.actuator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsMapContaining.hasEntry;
import static org.hamcrest.collection.IsMapContaining.hasKey;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
public class HealthCheckIntegrationTest {
@LocalManagementPort
private int port;
private RestTemplate restTemplate = new RestTemplate();
@Autowired
private ObjectMapper objectMapper;
@Test
public void whenSpringContextIsBootstrapped_thenActuatorHealthEndpointWorks() throws IOException {
ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:" + port + "/health", String.class);
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
Map<String, Object> response = objectMapper.readValue(entity.getBody(), new TypeReference<LinkedHashMap<String, Object>>() {
});
assertThat(response, hasEntry("status", "UP"));
assertThat(response, hasKey("myHealthCheck"));
assertThat(response, hasKey("diskSpace"));
}
}
@@ -0,0 +1,35 @@
package com.baeldung.actuator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class HealthCheckUnitTest {
@Test
public void whenCheckMethodReturnsZero_thenHealthMethodReturnsStatusUP() {
HealthCheck healthCheck = Mockito.spy(new HealthCheck());
when(healthCheck.check()).thenReturn(0);
Health health = healthCheck.health();
assertThat(health.getStatus(), is(Status.UP));
}
@Test
public void whenCheckMethodReturnsOtherThanZero_thenHealthMethodReturnsStatusDOWN() {
HealthCheck healthCheck = Mockito.spy(new HealthCheck());
when(healthCheck.check()).thenReturn(-1);
Health health = healthCheck.health();
assertThat(health.getStatus(), is(Status.DOWN));
}
}
@@ -0,0 +1,61 @@
package com.baeldung.actuator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "management.port=0")
public class LoginServiceIntegrationTest {
@LocalManagementPort
private int port;
@Autowired
private LoginServiceImpl loginService;
private RestTemplate restTemplate = new RestTemplate();
@Autowired
private ObjectMapper objectMapper;
@Test
public void whenLoginIsAdmin_thenSuccessCounterIsIncremented() throws IOException {
boolean success = loginService.login("admin", "secret".toCharArray());
ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class);
Map<String, Object> response = objectMapper.readValue(entity.getBody(), new TypeReference<HashMap<String, Object>>() {
});
assertThat(success, is(true));
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
assertThat(response, hasEntry("counter.login.success", 1));
}
@Test
public void whenLoginIsNotAdmin_thenFailureCounterIsIncremented() throws IOException {
boolean success = loginService.login("user", "notsecret".toCharArray());
ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:" + port + "/metrics", String.class);
Map<String, Object> response = objectMapper.readValue(entity.getBody(), new TypeReference<HashMap<String, Object>>() {
});
assertThat(success, is(false));
assertThat(entity.getStatusCode(), is(HttpStatus.OK));
assertThat(response, hasEntry("counter.login.failure", 1));
}
}
@@ -0,0 +1,41 @@
package com.baeldung.actuator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = LoginServiceImpl.class)
public class LoginServiceUnitTest {
@MockBean
CounterService counterService;
@Autowired
LoginServiceImpl loginService;
@Test
public void whenLoginUserIsAdmin_thenSuccessCounterIsIncremented() {
boolean loginResult = loginService.login("admin", "secret".toCharArray());
assertThat(loginResult, is(true));
verify(counterService, times(1)).increment("counter.login.success");
}
@Test
public void whenLoginUserIsNotAdmin_thenFailureCounterIsIncremented() {
boolean loginResult = loginService.login("user", "notsecret".toCharArray());
assertThat(loginResult, is(false));
verify(counterService, times(1)).increment("counter.login.failure");
}
}
@@ -36,7 +36,7 @@ public class UserController {
userRepository.save(user);
model.addAttribute("users", userRepository.findAll());
return "index";
return "redirect:/index";
}
@GetMapping("/edit/{id}")
@@ -55,7 +55,7 @@ public class UserController {
userRepository.save(user);
model.addAttribute("users", userRepository.findAll());
return "index";
return "redirect:/index";
}
@GetMapping("/delete/{id}")
@@ -41,7 +41,7 @@ public class UserControllerUnitTest {
when(mockedBindingResult.hasErrors()).thenReturn(false);
assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("index");
assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("redirect:/index");
}
@Test
@@ -64,7 +64,7 @@ public class UserControllerUnitTest {
when(mockedBindingResult.hasErrors()).thenReturn(false);
assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("index");
assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("redirect:/index");
}
@Test
@@ -41,10 +41,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
+3 -35
View File
@@ -14,38 +14,6 @@
<relativePath>../../parent-kotlin</relativePath>
</parent>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
@@ -142,9 +110,9 @@
<properties>
<kotlin.version>1.3.31</kotlin.version>
<r2dbc.version>1.0.0.M1</r2dbc.version>
<r2dbc-spi.version>1.0.0.M7</r2dbc-spi.version>
<h2-r2dbc.version>1.0.0.BUILD-SNAPSHOT</h2-r2dbc.version>
<r2dbc.version>1.0.0.RELEASE</r2dbc.version>
<r2dbc-spi.version>0.8.2.RELEASE</r2dbc-spi.version>
<h2-r2dbc.version>0.8.4.RELEASE</h2-r2dbc.version>
<kotlinx-coroutines.version>1.2.1</kotlinx-coroutines.version>
</properties>
@@ -2,12 +2,11 @@ package com.baeldung.nonblockingcoroutines.controller
import com.baeldung.nonblockingcoroutines.model.Product
import com.baeldung.nonblockingcoroutines.repository.ProductRepositoryCoroutines
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType.APPLICATION_JSON
@@ -15,7 +14,6 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBody
import org.springframework.web.reactive.function.client.awaitExchange
class ProductControllerCoroutines {
@Autowired
@@ -38,7 +36,7 @@ class ProductControllerCoroutines {
webClient.get()
.uri("/stock-service/product/$id/quantity")
.accept(APPLICATION_JSON)
.awaitExchange().awaitBody<Int>()
.retrieve().awaitBody<Int>()
}
ProductStockView(product.await()!!, quantity.await())
}
@@ -12,7 +12,6 @@ import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.awaitBody
import org.springframework.web.reactive.function.client.awaitExchange
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.bodyAndAwait
@@ -37,7 +36,7 @@ class ProductsHandler(
webClient.get()
.uri("/stock-service/product/$id/quantity")
.accept(MediaType.APPLICATION_JSON)
.awaitExchange().awaitBody<Int>()
.retrieve().awaitBody<Int>()
}
return ServerResponse.ok().json().bodyAndAwait(ProductStockView(product.await()!!, quantity.await()))
}
@@ -1,7 +1,7 @@
package com.baeldung.nonblockingcoroutines.repository
import com.baeldung.nonblockingcoroutines.model.Product
import org.springframework.data.r2dbc.function.DatabaseClient
import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@@ -10,7 +10,7 @@ import reactor.core.publisher.Mono
class ProductRepository(private val client: DatabaseClient) {
fun getProductById(id: Int): Mono<Product> {
return client.execute().sql("SELECT * FROM products WHERE id = $1")
return client.execute("SELECT * FROM products WHERE id = $1")
.bind(0, id)
.`as`(Product::class.java)
.fetch()
@@ -18,8 +18,7 @@ class ProductRepository(private val client: DatabaseClient) {
}
fun addNewProduct(name: String, price: Float): Mono<Void> {
return client.execute()
.sql("INSERT INTO products (name, price) VALUES($1, $2)")
return client.execute("INSERT INTO products (name, price) VALUES($1, $2)")
.bind(0, name)
.bind(1, price)
.then()
@@ -6,14 +6,14 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.reactive.awaitFirstOrNull
import kotlinx.coroutines.reactive.flow.asFlow
import org.springframework.data.r2dbc.function.DatabaseClient
import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.stereotype.Repository
@Repository
class ProductRepositoryCoroutines(private val client: DatabaseClient) {
suspend fun getProductById(id: Int): Product? =
client.execute().sql("SELECT * FROM products WHERE id = $1")
client.execute("SELECT * FROM products WHERE id = $1")
.bind(0, id)
.`as`(Product::class.java)
.fetch()
@@ -21,8 +21,7 @@ class ProductRepositoryCoroutines(private val client: DatabaseClient) {
.awaitFirstOrNull()
suspend fun addNewProduct(name: String, price: Float) =
client.execute()
.sql("INSERT INTO products (name, price) VALUES($1, $2)")
client.execute("INSERT INTO products (name, price) VALUES($1, $2)")
.bind(0, name)
.bind(1, price)
.then()
@@ -0,0 +1,7 @@
## Spring Boot MVC
This module contains articles about Spring Web MVC in Spring Boot projects.
### Relevant Articles:
- More articles: [[prev -->]](/spring-boot-modules/spring-boot-mvc-2)
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-mvc-3</artifactId>
<name>spring-boot-mvc-3</name>
<packaging>jar</packaging>
<description>Module For Spring Boot MVC Web</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,21 @@
package com.baeldung.circularviewpath;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Spring Boot launcher for an application
*
*/
@SpringBootApplication(scanBasePackages = "com.baeldung.controller.circularviewpath")
public class CircularViewPathApplication {
/**
* Launches a Spring Boot application
*
* @param args null
*/
public static void main(String[] args) {
SpringApplication.run(CircularViewPathApplication.class, args);
}
}
@@ -0,0 +1,16 @@
package com.baeldung.controller.circularviewpath;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class CircularViewPathController {
/**
* A request mapping which may cause circular view path exception
*/
@GetMapping("/path")
public String path() {
return "path";
}
}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>path.html</title>
</head>
<body>
<p>path.html</p>
</body>
</html>
@@ -0,0 +1,88 @@
package com.baeldung.properties.lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringListPropertiesApplication.class)
public class ListsPropertiesUnitTest {
@Value("${arrayOfStrings}")
private String[] arrayOfStrings;
@Value("${arrayOfStrings}")
private List<String> unexpectedListOfStrings;
@Value("#{'${arrayOfStrings}'.split(',')}")
private List<String> listOfStrings;
@Value("#{${listOfStrings}}")
private List<String> listOfStringsV2;
@Value("#{'${listOfStringsWithCustomDelimiter}'.split(';')}")
private List<String> listOfStringsWithCustomDelimiter;
@Value("#{'${listOfBooleans}'.split(',')}")
private List<Boolean> listOfBooleans;
@Value("#{'${listOfIntegers}'.split(',')}")
private List<Integer> listOfIntegers;
@Value("#{'${listOfCharacters}'.split(',')}")
private List<Character> listOfCharacters;
@Autowired
private Environment environment;
@Test
public void whenContextIsInitialized_thenInjectedArrayContainsExpectedValues() {
assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings);
}
@Test
public void whenContextIsInitialized_thenInjectedListContainsUnexpectedValues() {
assertEquals(Collections.singletonList("Baeldung,dot,com"), unexpectedListOfStrings);
}
@Test
public void whenContextIsInitialized_thenInjectedListContainsExpectedValues() {
assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings);
}
@Test
public void whenContextIsInitialized_thenInjectedListV2ContainsExpectedValues() {
assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsV2);
}
@Test
public void whenContextIsInitialized_thenInjectedListWithCustomDelimiterContainsExpectedValues() {
assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStringsWithCustomDelimiter);
}
@Test
public void whenContextIsInitialized_thenInjectedListOfBasicTypesContainsExpectedValues() {
assertEquals(Arrays.asList(false, false, true), listOfBooleans);
assertEquals(Arrays.asList(1, 2, 3, 4), listOfIntegers);
assertEquals(Arrays.asList('a', 'b', 'c'), listOfCharacters);
}
@Test
public void whenReadingFromSpringEnvironment_thenPropertiesHaveExpectedValues() {
String[] arrayOfStrings = environment.getProperty("arrayOfStrings", String[].class);
List<String> listOfStrings = (List<String>)environment.getProperty("arrayOfStrings", List.class);
assertEquals(new String[] {"Baeldung", "dot", "com"}, arrayOfStrings);
assertEquals(Arrays.asList("Baeldung", "dot", "com"), listOfStrings);
}
}
@@ -0,0 +1,10 @@
package com.baeldung.properties.lists;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource(value = "lists.properties")
public class SpringListPropertiesApplication {
}
@@ -0,0 +1,6 @@
arrayOfStrings=Baeldung,dot,com
listOfStrings={'Baeldung','dot','com'}
listOfStringsWithCustomDelimiter=Baeldung;dot;com
listOfBooleans=false,false,true
listOfIntegers=1,2,3,4
listOfCharacters=a,b,c
@@ -1,13 +0,0 @@
# Logs
logs
*.log
# Git
.git
.cache
# Classes
**/*.class
# Ignore md files
*.md
@@ -1,10 +0,0 @@
FROM maven:3.6.0-jdk-11
WORKDIR /code/spring-boot-modules/spring-boot-properties/
COPY ./spring-boot-modules/spring-boot-properties/pom.xml .
COPY ./spring-boot-modules/spring-boot-properties/src ./src
COPY ./parent-boot-2/pom.xml /code/parent-boot-2/pom.xml
COPY ./pom.xml /code/pom.xml
COPY ./custom-pmd-0.0.1.jar /code/custom-pmd-0.0.1.jar
COPY ./baeldung-pmd-rules.xml /code/baeldung-pmd-rules.xml
RUN mvn dependency:resolve
CMD ["mvn", "spring-boot:run"]
@@ -11,6 +11,9 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.baeldung.yaml.YAMLConfig.Idm;
import com.baeldung.yaml.YAMLConfig.Service;
@SpringBootApplication
public class MyApplication implements CommandLineRunner {
@@ -26,6 +29,23 @@ public class MyApplication implements CommandLineRunner {
System.out.println("using environment:" + myConfig.getEnvironment());
System.out.println("name:" + myConfig.getName());
System.out.println("servers:" + myConfig.getServers());
if ("testing".equalsIgnoreCase(myConfig.getEnvironment())) {
System.out.println("external:" + myConfig.getExternal());
System.out.println("map:" + myConfig.getMap());
Idm idm = myConfig.getComponent().getIdm();
Service service = myConfig.getComponent().getService();
System.out.println("Idm:");
System.out.println(" Url: " + idm.getUrl());
System.out.println(" User: " + idm.getUser());
System.out.println(" Password: " + idm.getPassword());
System.out.println(" Description: " + idm.getDescription());
System.out.println("Service:");
System.out.println(" Url: " + service.getUrl());
System.out.println(" Token: " + service.getToken());
System.out.println(" Description: " + service.getDescription());
}
}
}
@@ -1,7 +1,10 @@
package com.baeldung.yaml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -13,6 +16,9 @@ public class YAMLConfig {
private String name;
private String environment;
private List<String> servers = new ArrayList<String>();
private List<String> external = new ArrayList<String>();
private Map<String, String> map = new HashMap<String, String>();
private Component component = new Component();
public List<String> getServers() {
return servers;
@@ -37,5 +43,111 @@ public class YAMLConfig {
public void setEnvironment(String environment) {
this.environment = environment;
}
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public List<String> getExternal() {
return external;
}
public void setExternal(List<String> external) {
this.external = external;
}
public class Component {
private Idm idm = new Idm();
private Service service = new Service();
public Idm getIdm() {
return idm;
}
public void setIdm(Idm idm) {
this.idm = idm;
}
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
}
public class Idm {
private String url;
private String user;
private String password;
private String description;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public class Service {
private String url;
private String token;
private String description;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
@@ -6,22 +6,42 @@ spring:
---
spring:
profiles: test
profiles: test
name: test-YAML
environment: testing
servers:
- www.abc.test.com
- www.xyz.test.com
- www.abc.test.com
- www.xyz.test.com
external: [www.abc.test.com, www.xyz.test.com]
map:
firstkey: key1
secondkey: key2
component:
idm:
url: myurl
user: user
password: password
description: >
this should be a long
description
service:
url: myurlservice
token: token
description: >
this should be another long
description
---
spring:
profiles: prod
profiles: prod
name: prod-YAML
environment: production
servers:
- www.abc.com
- www.xyz.com
- www.abc.com
- www.xyz.com
---
@@ -11,6 +11,7 @@ import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class)
@TestPropertySource(properties = {"spring.profiles.active = test"})
class YAMLIntegrationTest {
@Autowired
@@ -20,5 +21,6 @@ class YAMLIntegrationTest {
void whenProfileTest_thenNameTesting() {
assertTrue("testing".equalsIgnoreCase(config.getEnvironment()));
assertTrue("test-YAML".equalsIgnoreCase(config.getName()));
assertTrue("myurl".equalsIgnoreCase(config.getComponent().getIdm().getUrl()));
}
}