Merge branch 'master' of https://github.com/thombergs/code-examples into circuitbreaker

This commit is contained in:
Saajan
2020-12-14 12:30:10 +05:30
76 changed files with 2674 additions and 294 deletions

View File

@@ -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();
}
}

BIN
aws/cdk/.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -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

0
aws/cdk/mvnw vendored Normal file
View File

182
aws/cdk/mvnw.cmd vendored Normal file
View File

@@ -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%

View File

@@ -0,0 +1,23 @@
package com.myorg;
import software.amazon.awscdk.core.Construct;
import software.amazon.awscdk.core.Stack;
import software.amazon.awscdk.core.StackProps;
import software.amazon.awscdk.services.s3.Bucket;
public class SecondStack extends Stack {
public SecondStack(final Construct scope, final String id) {
this(scope, id, null);
}
public SecondStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
String bucketName = (String) this.getNode().tryGetContext("bucketName2");
Bucket bucket = Bucket.Builder.create(this, "bucket")
.bucketName(bucketName)
.build();
}
}

View File

@@ -86,6 +86,9 @@ if [[ "$MODULE" == "module5" ]]
then
# ADD NEW MODULES HERE
# (add new modules above the rest so you get quicker feedback if it fails)
build_gradle_module "spring-boot/spring-boot-mocking-modules"
build_gradle_module "spring-boot/specification"
build_gradle_module "spring-boot/hibernate-search"
build_maven_module "core-java/streams/fileswithstreams"
build_maven_module "spring-boot/spring-boot-health-check"
build_maven_module "spring-boot/spring-boot-logging-2"

View File

@@ -1,120 +0,0 @@
{
"provider": {
"name": "customerServiceProvider"
},
"consumer": {
"name": "addressClient"
},
"interactions": [
{
"description": "a request to the address collection resource",
"request": {
"method": "GET",
"path": "/addresses/"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"_embedded": {
"addresses": [
{
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
},
{
"street": "High Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/2"
},
"address": {
"href": "http://localhost:8080/addresses/2"
},
"customer": {
"href": "http://localhost:8080/addresses/2/customer"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/addresses{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/addresses"
},
"search": {
"href": "http://localhost:8080/addresses/search"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
},
"providerStates": [
{
"name": "a collection of 2 addresses"
}
]
},
{
"description": "a request to the address resource",
"request": {
"method": "GET",
"path": "/addresses/1"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
}
},
"providerStates": [
{
"name": "a single address"
}
]
}
],
"metadata": {
"pact-specification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.2"
}
}
}

View File

@@ -1,115 +0,0 @@
{
"provider": {
"name": "userservice"
},
"consumer": {
"name": "userclient"
},
"interactions": [
{
"description": "a request to PUT a person",
"request": {
"method": "PUT",
"path": "/user-service/users/42"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"firstName": "Zaphod",
"lastName": "Beeblebrox"
},
"matchingRules": {
"body": {
"$.firstName": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.lastName": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
},
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json"
}
],
"combine": "AND"
}
}
}
},
"providerStates": [
{
"name": "person 42 exists"
}
]
},
{
"description": "a request to POST a person",
"request": {
"method": "POST",
"path": "/user-service/users"
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"body": {
"id": 42
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json"
}
],
"combine": "AND"
}
},
"body": {
"$.id": {
"matchers": [
{
"match": "integer"
}
],
"combine": "AND"
}
}
}
},
"providerStates": [
{
"name": "provider accepts a new person"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.20"
}
}
}

View File

@@ -1,59 +0,0 @@
{
"consumer": {
"name": "userclient"
},
"provider": {
"name": "userservice"
},
"messages": [
{
"description": "a user created message",
"metaData": {
"Content-Type": "application/json; charset=UTF-8"
},
"contents": {
"messageUuid": "string",
"user": {
"id": 42,
"name": "Zaphod Beeblebrox"
}
},
"matchingRules": {
"body": {
"$.messageUuid": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.user.id": {
"matchers": [
{
"match": "number"
}
],
"combine": "AND"
},
"$.user.name": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.20"
}
}
}

View File

@@ -0,0 +1,5 @@
# Hibernate Search Tutorial
This example code demonstrates the following:
* How to configure Hibernate search with Elasticsearch integration
* How to configure entities for indexing
* Some examples to queries

View File

@@ -0,0 +1,40 @@
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'io.reflectoring'
version = '1.0.0'
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-web'
implementation 'org.hibernate:hibernate-search-orm:5.11.5.Final'
implementation 'org.hibernate:hibernate-search-elasticsearch:5.11.5.Final'
implementation 'org.mapstruct:mapstruct:1.3.1.Final'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.3.1.Final'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Mon Sep 07 12:46:19 IST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

184
spring-boot/hibernate-search/gradlew vendored Normal file
View File

@@ -0,0 +1,184 @@
#!/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" "$@"

104
spring-boot/hibernate-search/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,104 @@
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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

View File

@@ -0,0 +1 @@
rootProject.name = 'hibernate-search'

View File

@@ -0,0 +1,15 @@
package io.reflectoring.hibernatesearch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HibernateSearchApplication {
public static void main(String[] args) {
SpringApplication.run(HibernateSearchApplication.class, args);
}
}

View File

@@ -0,0 +1,25 @@
package io.reflectoring.hibernatesearch.configuration;
import io.reflectoring.hibernatesearch.service.IndexingService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@RequiredArgsConstructor
public class StartupEvent implements ApplicationListener<ApplicationReadyEvent> {
private final IndexingService service;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
service.initiateIndexing();
} catch (InterruptedException e) {
log.error("Failed to reindex entities ",e);
}
}
}

View File

@@ -0,0 +1,63 @@
package io.reflectoring.hibernatesearch.controller;
import io.reflectoring.hibernatesearch.controller.dto.PostResponse;
import io.reflectoring.hibernatesearch.controller.dto.UserResponse;
import io.reflectoring.hibernatesearch.controller.mapper.PostMapper;
import io.reflectoring.hibernatesearch.controller.mapper.UserMapper;
import io.reflectoring.hibernatesearch.service.IndexingService;
import io.reflectoring.hibernatesearch.service.SearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@Slf4j
@RequiredArgsConstructor
class IndexController {
private final IndexingService indexingService;
private final SearchService searchService;
private final PostMapper postMapper;
private final UserMapper userMapper;
@PostMapping("/reindex")
public void reindex() throws InterruptedException {
indexingService.initiateIndexing();
}
@GetMapping("/user")
public List<UserResponse> getUser(@RequestParam String first,
@RequestParam Integer max,
@RequestParam Integer page){
return searchService.getUserByFirst(first, max, page)
.stream().map(userMapper::toResponse).collect(Collectors.toList());
}
@GetMapping("/user/projection")
public List<UserResponse> getProjectUser(@RequestParam String first,
@RequestParam Integer max,
@RequestParam Integer page){
return searchService.getUserByFirstWithProjection(first, max, page)
.stream().map(userMapper::toResponse).collect(Collectors.toList());
}
@GetMapping("/post")
public List<PostResponse> getPost(@RequestParam Long likeCount,
@RequestParam(required = false) String hashTags,
@RequestParam(required = false) String tag){
return searchService.getBasedOnLikeCountTags(likeCount, hashTags, tag)
.stream().map(postMapper::toResponse).collect(Collectors.toList());
}
@GetMapping("/post/word")
public List<PostResponse> getPostByWord(@RequestParam String word){
return searchService.getPostBasedOnWord(word)
.stream().map(postMapper::toResponse).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,21 @@
package io.reflectoring.hibernatesearch.controller.dto;
import io.reflectoring.hibernatesearch.domain.Tag;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class PostResponse {
private String id;
private String body;
private UserResponse user;
private Tag tag;
private String imageUrl;
private String imageDescription;
private String hashTags;
private long likeCount;
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,14 @@
package io.reflectoring.hibernatesearch.controller.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserResponse {
private String id;
private String first;
private String middle;
private String last;
private Integer age;
}

View File

@@ -0,0 +1,10 @@
package io.reflectoring.hibernatesearch.controller.mapper;
import io.reflectoring.hibernatesearch.controller.dto.PostResponse;
import io.reflectoring.hibernatesearch.domain.Post;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring", uses = UserMapper.class)
public interface PostMapper {
PostResponse toResponse(Post post);
}

View File

@@ -0,0 +1,10 @@
package io.reflectoring.hibernatesearch.controller.mapper;
import io.reflectoring.hibernatesearch.controller.dto.UserResponse;
import io.reflectoring.hibernatesearch.domain.User;
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public interface UserMapper {
UserResponse toResponse(User user);
}

View File

@@ -0,0 +1,47 @@
package io.reflectoring.hibernatesearch.domain;
import lombok.Getter;
import lombok.Setter;
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.hibernate.search.annotations.*;
import javax.persistence.*;
import java.time.LocalDateTime;
@Getter
@Setter
@Entity
@Indexed(index = "idx_post")
@NormalizerDef(name = "lower",
filters = @TokenFilterDef(factory = LowerCaseFilterFactory.class))
public class Post {
@Id
private String id;
@Field(name = "body")
@Field(name = "bodyFiltered", analyzer = @Analyzer(definition = "stop"))
private String body;
@ManyToOne
@IndexedEmbedded
private User user;
@Field(normalizer = @Normalizer(definition = "lower"))
@Enumerated(EnumType.STRING)
private Tag tag;
private String imageUrl;
private String imageDescription;
@Field
private String hashTags;
@Field
@SortableField
private long likeCount;
@Field(analyze = Analyze.NO)
@SortableField
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,7 @@
package io.reflectoring.hibernatesearch.domain;
public enum Tag {
MOVIE,
MUSIC,
LITERATURE
}

View File

@@ -0,0 +1,37 @@
package io.reflectoring.hibernatesearch.domain;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.search.annotations.*;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;
@Getter
@Setter
@Entity
@Indexed(index = "idx_user")
public class User {
@Id
private String id;
@Field(store = Store.YES)
@Field(name = "fullName")
private String first;
@Field(index = Index.NO, store = Store.YES)
private String middle;
@Field(store = Store.YES)
@Field(name = "fullName")
private String last;
@Field
private Integer age;
@ContainedIn
@OneToMany(mappedBy = "user")
private List<Post> post;
}

View File

@@ -0,0 +1,26 @@
package io.reflectoring.hibernatesearch.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
@Service
@RequiredArgsConstructor
@Slf4j
public class IndexingService {
private final EntityManager em;
@Transactional
public void initiateIndexing() throws InterruptedException {
log.info("Initiating indexing...");
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
fullTextEntityManager.createIndexer().startAndWait();
log.info("All entities indexed");
}
}

View File

@@ -0,0 +1,95 @@
package io.reflectoring.hibernatesearch.service;
import io.reflectoring.hibernatesearch.domain.Post;
import io.reflectoring.hibernatesearch.domain.User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.Query;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.List;
@Component
@Slf4j
@RequiredArgsConstructor
public class SearchService {
private final EntityManager entityManager;
public List<Post> getBasedOnLikeCountTags(Long likeCount, String hashTags, String tag){
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
.forEntity(Post.class)
.get();
Query likeCountGreater = qb.range().onField("likeCount").above(likeCount).createQuery();
Query hashTagsQuery = qb.keyword().onField("hashTags").matching(hashTags).createQuery();
Query tagQuery = qb.keyword().onField("tag").matching(tag).createQuery();
Query finalQuery = qb.bool().must(likeCountGreater).should(tagQuery).should(hashTagsQuery).createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(finalQuery, Post.class);
fullTextQuery.setSort(qb.sort().byScore().createSort());
return (List<Post>) fullTextQuery.getResultList();
}
public List<Post> getPostBasedOnWord(String word){
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
.forEntity(Post.class)
.get();
Query foodQuery = qb.keyword().onFields("bodyFiltered","hashTags").matching(word).createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(foodQuery, Post.class);
return (List<Post>) fullTextQuery.getResultList();
}
public List<User> getUserByFirst(String first, int max, int page){
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
.forEntity(User.class)
.get();
Query similarToUser = qb.keyword().wildcard().onField("first")
.matching(first).createQuery();
Query finalQuery = qb.bool().must(similarToUser).createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(finalQuery, User.class);
fullTextQuery.setSort(qb.sort().byField("age").desc().andByScore().createSort());
fullTextQuery.setMaxResults(max);
fullTextQuery.setFirstResult(page);
return (List<User>) fullTextQuery.getResultList();
}
public List<User> getUserByFirstWithProjection(String first, int max, int page){
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
.forEntity(User.class)
.get();
Query similarToUser = qb.keyword().fuzzy().withEditDistanceUpTo(2).onField("first")
.matching(first).createQuery();
Query finalQuery = qb.bool().must(similarToUser).createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(finalQuery, User.class);
fullTextQuery.setProjection(FullTextQuery.ID, "first","last","middle","age");
fullTextQuery.setSort(qb.sort().byField("age").desc().andByScore().createSort());
fullTextQuery.setMaxResults(max);
fullTextQuery.setFirstResult(page);
return getUserList(fullTextQuery.getResultList());
}
private List<User> getUserList(List<Object[]> resultList) {
List<User> users = new ArrayList<>();
for (Object[] objects : resultList) {
User user = new User();
user.setId((String) objects[0]);
user.setFirst((String) objects[1]);
user.setLast((String) objects[2]);
user.setMiddle((String) objects[3]);
user.setAge((Integer) objects[4]);
users.add(user);
}
return users;
}
}

View File

@@ -0,0 +1,15 @@
spring:
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: create-drop
properties:
hibernate:
search:
default:
indexmanager: elasticsearch
elasticsearch:
host: http://192.168.0.103:9200
index_schema_management_strategy: drop-and-create
required_index_status: yellow

View File

@@ -0,0 +1,47 @@
insert into user (id, first, last, middle, age) values ( 'c_1', 'John', 'Doe', 'Maven' , 31);
insert into user (id, first, last, middle, age) values ( 'c_2', 'Jane', 'Dove', 'Gradle', 41);
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_1', 'The tattered work gloves speak of the many hours of hard labor he endured throughout his life', now(),
'#work#labour', 'Office', 'uri://sample1', 20, 'LITERATURE', 'c_1' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_2', 'Dolores wouldn''t have eaten the meal if she had known what it actually was.', now(),
'#food', 'Office', 'uri://sample1', 7686, 'MUSIC', 'c_1' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_3', 'It caught him off guard that space smelled of seared steak.', now(),
'#food#stale#caughtinact', 'Office', 'uri://sample1', 1234, 'LITERATURE', 'c_1' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_4', '25 years later, she still regretted that specific moment.', now(),
'#ages', 'Office', 'uri://sample1', 679, 'MOVIE', 'c_1' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_5', 'She had a habit of taking showers in lemonade.', now(),
'#bath', 'Office', 'uri://sample1', 23, 'LITERATURE', 'c_1' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_6', 'She is never happy until she finds something to be unhappy about; then, she is overjoyed.', now(),
'#feelings', 'Office', 'uri://sample1', 4569877, 'MOVIE', 'c_2' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_7', 'They throw cabbage that turns your brain into emotional baggage.', now(),
'#food', 'Office', 'uri://sample1', 1000, 'MUSIC', 'c_2' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_8', 'There''s a message for you if you look up.', now(),
'#message', 'Office', 'uri://sample1', 20, 'LITERATURE', 'c_2' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_9', 'Please wait outside of the house.', now(),
'#house#labour', 'Office', 'uri://sample1', 40, 'MUSIC', 'c_2' );
insert into post (id, body, created_at, hash_tags, image_description, image_url, like_count, tag, user_id)
values ( 'p_10', 'Honestly, I didn''t care much for the first season, so I didn''t bother with the second.', now(),
'#honest#season#time', 'Office', 'uri://sample1', 20000, 'MOVIE', 'c_2' );

34
spring-boot/specification/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,4 @@
# Intro to specification Tutorial
This example code demonstrates the following:
* How to write queries with Specification
* How to generate type safe queries dynamically with Specification

View File

@@ -0,0 +1,35 @@
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'io.reflectoring'
version = '1.0.0'
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-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.hibernate:hibernate-jpamodelgen'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
#Sun Oct 18 17:44:53 IST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

184
spring-boot/specification/gradlew vendored Normal file
View File

@@ -0,0 +1,184 @@
#!/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" "$@"

89
spring-boot/specification/gradlew.bat vendored Normal file
View File

@@ -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

View File

@@ -0,0 +1 @@
rootProject.name = 'specification'

View File

@@ -0,0 +1,13 @@
package io.reflectoring.specification;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpecificationApplication {
public static void main(String[] args) {
SpringApplication.run(SpecificationApplication.class, args);
}
}

View File

@@ -0,0 +1,28 @@
package io.reflectoring.specification.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
@Entity
@Getter
@Setter
public class Address {
@Id
private String id;
private String line1;
private String line2;
private String city;
@Enumerated(EnumType.STRING)
private STATE state;
private String pincode;
}

View File

@@ -0,0 +1,10 @@
package io.reflectoring.specification.model;
public enum Category {
MOBILE,
TV_APPLIANCES,
MEN_FASHION,
WOMEN_FASHION,
BOOKS,
BEAUTY
}

View File

@@ -0,0 +1,22 @@
package io.reflectoring.specification.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
@Entity
@Getter
@Setter
public class Distributor {
@Id
private String id;
private String name;
@OneToOne
private Address address;
}

View File

@@ -0,0 +1,47 @@
package io.reflectoring.specification.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
@Entity
@Getter
@Setter
public class Product {
@Id
private String id;
private String name;
private Double price;
private LocalDateTime manufacturingDate;
@ManyToOne
private Address manufacturingPlace;
private Double weight;
@Embedded
private Dimension dimension;
@ManyToOne
private Distributor distributor;
@Enumerated(EnumType.STRING)
private Category category;
@Embeddable
@Getter
@Setter
public static class Dimension {
private Double height;
private Double width;
}
}

View File

@@ -0,0 +1,6 @@
package io.reflectoring.specification.model;
public enum STATE {
TEXAS,
CALIFORNIA
}

View File

@@ -0,0 +1,121 @@
package io.reflectoring.specification.repository;
import io.reflectoring.specification.model.*;
import lombok.RequiredArgsConstructor;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.data.jpa.domain.Specification.where;
@Component
@RequiredArgsConstructor
public class CustomProductRepository {
private static final Double PREMIUM_PRICE = 1000D;
private final ProductRepository productRepository;
public List<Product> getLowRangeProducts(List<Category> categories) {
return productRepository.findAll(where(belongsToCategory(categories)).and(pricesAreBetween(0D, PREMIUM_PRICE)));
}
public List<Product> getPremiumProducts(List<Category> categories) {
return productRepository.findAll(
where(belongsToCategory(categories)).and(isPremium()));
}
public List<Product> getPremiumProducts(String name, List<Category> categories) {
return productRepository.findAll(
where(belongsToCategory(categories))
.and(nameLike(name))
.and(isPremium()));
}
public List<Product> getQueryResult(List<Filter> filters){
if(filters.size()>0) {
return productRepository.findAll(getSpecificationFromFilters(filters));
}else {
return productRepository.findAll();
}
}
private Specification<Product> getSpecificationFromFilters(List<Filter> filter) {
Specification<Product> specification = where(createSpecification(filter.remove(0)));
for (Filter input : filter) {
specification = specification.and(createSpecification(input));
}
return specification;
}
private Specification<Product> createSpecification(Filter input) {
switch (input.getOperator()){
case EQUALS:
return (root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get(input.getField()),
castToRequiredType(root.get(input.getField()).getJavaType(), input.getValue()));
case NOT_EQ:
return (root, query, criteriaBuilder) ->
criteriaBuilder.notEqual(root.get(input.getField()),
castToRequiredType(root.get(input.getField()).getJavaType(), input.getValue()));
case GREATER_THAN:
return (root, query, criteriaBuilder) ->
criteriaBuilder.gt(root.get(input.getField()),
(Number) castToRequiredType(root.get(input.getField()).getJavaType(), input.getValue()));
case LESS_THAN:
return (root, query, criteriaBuilder) ->
criteriaBuilder.lt(root.get(input.getField()),
(Number) castToRequiredType(root.get(input.getField()).getJavaType(), input.getValue()));
case LIKE:
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.get(input.getField()), "%"+input.getValue()+"%");
case IN:
return (root, query, criteriaBuilder) ->
criteriaBuilder.in(root.get(input.getField()))
.value(castToRequiredType(root.get(input.getField()).getJavaType(), input.getValues()));
default:
throw new RuntimeException("Operation not supported yet");
}
}
private Object castToRequiredType(Class fieldType, String value) {
if(fieldType.isAssignableFrom(Double.class)){
return Double.valueOf(value);
}else if(fieldType.isAssignableFrom(Integer.class)){
return Integer.valueOf(value);
}else if(Enum.class.isAssignableFrom(fieldType)){
return Enum.valueOf(fieldType, value);
}
return null;
}
private Object castToRequiredType(Class fieldType, List<String> value) {
List<Object> lists = new ArrayList<>();
for (String s : value) {
lists.add(castToRequiredType(fieldType, s));
}
return lists;
}
private Specification<Product> nameLike(String name){
return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get(Product_.NAME), "%"+name+"%");
}
private Specification<Product> pricesAreBetween(Double min, Double max){
return (root, query, criteriaBuilder)-> criteriaBuilder.between(root.get(Product_.PRICE), min, max);
}
private Specification<Product> belongsToCategory(List<Category> categories){
return (root, query, criteriaBuilder)-> criteriaBuilder.in(root.get(Product_.CATEGORY)).value(categories);
}
private Specification<Product> isPremium() {
return (root, query, criteriaBuilder) ->
criteriaBuilder.and(
criteriaBuilder.equal(root.get(Product_.MANUFACTURING_PLACE).get(Address_.STATE),
STATE.CALIFORNIA),
criteriaBuilder.greaterThanOrEqualTo(root.get(Product_.PRICE), PREMIUM_PRICE));
}
}

View File

@@ -0,0 +1,17 @@
package io.reflectoring.specification.repository;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
@Builder
public class Filter {
private String field;
private QueryOperator operator;
private String value;
private List<String> values;//Used in case of IN operator
}

View File

@@ -0,0 +1,31 @@
package io.reflectoring.specification.repository;
import io.reflectoring.specification.model.Category;
import io.reflectoring.specification.model.Product;
import io.reflectoring.specification.model.STATE;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
interface ProductRepository extends JpaRepository<Product, String>, JpaSpecificationExecutor<Product> {
List<Product> findAllByNameLike(String name);
List<Product> findAllByNameLikeAndPriceLessThanEqual(String name, Double price);
List<Product> findAllByCategoryInAndPriceLessThanEqual(List<Category> categories, Double price);
List<Product> findAllByCategoryInAndPriceBetween(List<Category> categories,
Double bottom, Double top);
List<Product> findAllByNameLikeAndCategoryIn(String name, List<Category> categories);
List<Product> findAllByNameLikeAndCategoryInAndPriceBetween(String name, List<Category> categories,
Double bottom, Double top);
List<Product> findAllByNameLikeAndCategoryInAndPriceBetweenAndManufacturingPlace_State(String name,
List<Category> categories,
Double bottom, Double top,
STATE state);
}

View File

@@ -0,0 +1,10 @@
package io.reflectoring.specification.repository;
public enum QueryOperator {
GREATER_THAN,
LESS_THAN,
EQUALS,
LIKE,
NOT_EQ,
IN
}

View File

@@ -0,0 +1,6 @@
spring:
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: create-drop

View File

@@ -0,0 +1,45 @@
insert into address (id, city, state, pincode) values
('addr_1', 'chicago', 'TEXAS', 'AB9898');
insert into address (id, city, state, pincode) values
('addr_2', 'hells kitchen', 'CALIFORNIA', 'AB9899');
insert into address (id, city, state, pincode) values
('addr_3', 'palm city', 'TEXAS', 'AA9800');
insert into address (id, city, state, pincode) values
('addr_4', ' San Diego', 'CALIFORNIA', 'AA9230');
insert into address (id, city, state, pincode) values
('addr_5', 'palm city', 'TEXAS', 'AA9560');
insert into distributor (id, name, address_id) values
('dist_1', 'john doe', 'addr_1');
insert into distributor (id, name, address_id) values
('dist_2', 'Max well', 'addr_2');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_1', 'one plus 8T', 100, now(), 'addr_3', 2.2, 3.4, 5.4, 'dist_1', 'MOBILE');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_2', 'Samsung 100 S', 200, now(), 'addr_4', 5.6, 7.9, 4.3, 'dist_2', 'MOBILE');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_3', 'Armani jacket size 32', 1100, now(), 'addr_4', 1.3, 10.4, 29.5, 'dist_2', 'MEN_FASHION');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_4', 'Zara purse', 500, now(), 'addr_5', 3.7, 50.6, 70.6, 'dist_1', 'WOMEN_FASHION');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_5', 'Sony Bravia ', 2000, now(), 'addr_5', 25.5, 48.8, 25.9, 'dist_1', 'TV_APPLIANCES');
insert into product
(id, name, price, manufacturing_date, manufacturing_place_id, weight, height, width, distributor_id, category)
values
('prod_6', 'zara jacket green color', 1500, now(), 'addr_4', 1.3, 10.4, 29.5, 'dist_2', 'MEN_FASHION');

View File

@@ -0,0 +1,13 @@
package io.reflectoring.specification;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpecificationApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -0,0 +1,79 @@
package io.reflectoring.specification.repository;
import io.reflectoring.specification.model.Category;
import io.reflectoring.specification.model.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CustomProductRepositoryTest {
@Autowired
private CustomProductRepository productRepository;
@Test
void getLowRangeProducts() {
List<Product> products = productRepository.getLowRangeProducts(List.of(Category.MOBILE, Category.TV_APPLIANCES));
assertEquals(2, products.size());
}
@Test
void getPremiumProducts() {
List<Product> products = productRepository.getPremiumProducts(List.of(Category.MEN_FASHION, Category.WOMEN_FASHION));
assertEquals(2, products.size());
}
@Test
void testGetPremiumProducts() {
List<Product> products = productRepository.getPremiumProducts("jacket", List.of(Category.MEN_FASHION));
assertEquals(2, products.size());
}
@Test
void testDynamicSpecification() {
Filter nameLike = Filter.builder()
.field("name")
.operator(QueryOperator.LIKE)
.value("jacket")
.build();
Filter categories = Filter.builder()
.field("category")
.operator(QueryOperator.IN)
.values(List.of(Category.MEN_FASHION.name(), Category.WOMEN_FASHION.name()))
.build();
List<Filter> filters = new ArrayList<>();
filters.add(nameLike);
filters.add(categories);
List<Product> products = productRepository.getQueryResult(filters);
assertEquals(2, products.size());
Filter lowRange = Filter.builder()
.field("price")
.operator(QueryOperator.LESS_THAN)
.value("1000")
.build();
categories.setValues(List.of(Category.MOBILE.name(), Category.TV_APPLIANCES.name()));
filters = new ArrayList<>();
filters.add(lowRange);
filters.add(categories);
products = productRepository.getQueryResult(filters);
assertEquals(2, products.size());
Filter priceEquals = Filter.builder()
.field("price")
.operator(QueryOperator.EQUALS)
.value("1100")
.build();
filters = new ArrayList<>();
filters.add(priceEquals);
products = productRepository.getQueryResult(filters);
assertEquals(1, products.size());
}
}

View File

@@ -0,0 +1,23 @@
plugins {
id 'org.springframework.boot' version '2.4.0'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'io.reflectoring.modules'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}

View File

@@ -0,0 +1,6 @@
#Tue Feb 06 12:27:20 CET 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip

View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## 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=""
# 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, switch paths to Windows format before running java
if $cygwin ; 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=$((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"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@@ -0,0 +1,84 @@
@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 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=
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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

View File

@@ -0,0 +1 @@
rootProject.name = 'mocking-modules'

View File

@@ -0,0 +1,15 @@
package io.reflectoring.modules;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan()
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

View File

@@ -0,0 +1,7 @@
package io.reflectoring.modules.github.api;
public interface GitHubMutations {
String createRepository(String token, GitHubRepository repository);
}

View File

@@ -0,0 +1,13 @@
package io.reflectoring.modules.github.api;
import java.util.List;
public interface GitHubQueries {
List<String> getOrganisations(String token);
List<String> getRepositories(String token, String organisation);
boolean repositoryExists(String token, String repositoryName, String organisation);
}

View File

@@ -0,0 +1,24 @@
package io.reflectoring.modules.github.api;
import java.util.Objects;
public class GitHubRepository {
private final String name;
private final String organization;
public GitHubRepository(String name, String organization) {
Objects.requireNonNull(name);
Objects.requireNonNull(organization);
this.name = name;
this.organization = organization;
}
public String getName() {
return name;
}
public String getOrganization() {
return organization;
}
}

View File

@@ -0,0 +1,14 @@
package io.reflectoring.modules.github.internal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class GitHubModuleConfiguration {
@Bean
GitHubService gitHubService(){
return new GitHubService();
}
}

View File

@@ -0,0 +1,35 @@
package io.reflectoring.modules.github.internal;
import io.reflectoring.modules.github.api.GitHubMutations;
import io.reflectoring.modules.github.api.GitHubQueries;
import io.reflectoring.modules.github.api.GitHubRepository;
import java.util.Collections;
import java.util.List;
public class GitHubService implements GitHubMutations, GitHubQueries {
@Override
public String createRepository(String token, GitHubRepository repository) {
// call the GitHub API to create a repo
return "https://github.com/reflectoring/reflectoring.github.io";
}
@Override
public List<String> getOrganisations(String token) {
// call the GitHub API to get a list of organisations for the user
return Collections.emptyList();
}
@Override
public List<String> getRepositories(String token, String organisation) {
// call the GitHub API to get a list of repositories for the user
return Collections.emptyList();
}
@Override
public boolean repositoryExists(String token, String repositoryName, String organisation) {
// call the GitHub API to find out if a given repo exists
return false;
}
}

View File

@@ -0,0 +1,7 @@
package io.reflectoring.modules.mail.api;
public interface EmailNotificationService {
void sendEmail(String to, String subject, String text);
}

View File

@@ -0,0 +1,28 @@
package io.reflectoring.modules.mail.internal;
import io.reflectoring.modules.mail.api.EmailNotificationService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(EmailModuleConfigurationProperties.class)
public class EmailModuleConfiguration {
private final EmailModuleConfigurationProperties configurationProperties;
public EmailModuleConfiguration(EmailModuleConfigurationProperties configurationProperties) {
this.configurationProperties = configurationProperties;
}
@Bean
EmailNotificationService emailNotificationService(MailClient mailClient) {
return new EmailNotificationServiceImpl(mailClient);
}
@Bean
MailClient mailClient() {
return new MailClient(configurationProperties.isEnabled());
}
}

View File

@@ -0,0 +1,17 @@
package io.reflectoring.modules.mail.internal;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "mail")
public class EmailModuleConfigurationProperties {
private boolean enabled;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View File

@@ -0,0 +1,19 @@
package io.reflectoring.modules.mail.internal;
import io.reflectoring.modules.mail.api.EmailNotificationService;
import org.springframework.stereotype.Component;
class EmailNotificationServiceImpl implements EmailNotificationService {
private final MailClient mailClient;
public EmailNotificationServiceImpl(MailClient mailClient) {
this.mailClient = mailClient;
}
@Override
public void sendEmail(String to, String subject, String text) {
// send an email
}
}

View File

@@ -0,0 +1,32 @@
package io.reflectoring.modules.mail.internal;
import javax.annotation.PostConstruct;
/**
* Internal mail server connection. This is not part of the API of the `mail` component.
*/
class MailClient {
private final boolean connectToMailServer;
public MailClient(boolean connectToMailServer) {
this.connectToMailServer = connectToMailServer;
}
void sendMail() {
}
/**
* There are many cases where a Spring application connects to external resources during startup, for example when
* initializing a connection pool to a database or some other service. This method just simulates a failing connection
* during startup. This forces us not to load this bean during integration tests.
*/
@PostConstruct
void connectToMailServer() {
if(connectToMailServer) {
throw new RuntimeException("Could not connect to mail server!");
}
}
}

View File

@@ -0,0 +1,46 @@
package io.reflectoring.modules.rest.internal;
import io.reflectoring.modules.github.api.GitHubMutations;
import io.reflectoring.modules.github.api.GitHubQueries;
import io.reflectoring.modules.github.api.GitHubRepository;
import io.reflectoring.modules.mail.api.EmailNotificationService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
class RepositoryController {
private final GitHubMutations gitHubMutations;
private final GitHubQueries gitHubQueries;
private final EmailNotificationService emailNotificationService;
public RepositoryController(
GitHubMutations gitHubMutations,
GitHubQueries gitHubQueries,
EmailNotificationService emailNotificationService
) {
this.gitHubMutations = gitHubMutations;
this.gitHubQueries = gitHubQueries;
this.emailNotificationService = emailNotificationService;
}
@PostMapping("/github/repository")
ResponseEntity<Void> createGitHubRepository(
@RequestParam("token") String token,
@RequestParam("repositoryName") String repoName,
@RequestParam("organizationName") String orgName
) {
if (gitHubQueries.repositoryExists(token, repoName, orgName)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
String repoUrl = gitHubMutations.createRepository(token, new GitHubRepository(repoName, orgName));
emailNotificationService.sendEmail("user@mail.com", "Your new repository", "Here's your new repository: " + repoUrl);
return ResponseEntity.ok().build();
}
}

View File

@@ -0,0 +1,51 @@
package io.reflectoring.modules.github;
import io.reflectoring.modules.github.internal.GitHubService;
import org.mockito.Mockito;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@TestConfiguration
public class GitHubModuleMock {
private final GitHubService gitHubServiceMock = Mockito.mock(GitHubService.class);
@Bean
@Primary
GitHubService gitHubServiceMock() {
return gitHubServiceMock;
}
public void givenCreateRepositoryReturnsUrl(String url) {
given(gitHubServiceMock.createRepository(any(), any())).willReturn(url);
}
public void givenRepositoryExists(){
given(gitHubServiceMock.repositoryExists(anyString(), anyString(), anyString())).willReturn(true);
}
public void givenRepositoryDoesNotExist(){
given(gitHubServiceMock.repositoryExists(anyString(), anyString(), anyString())).willReturn(false);
}
public void assertRepositoryCreated(){
verify(gitHubServiceMock).createRepository(any(), any());
}
public void givenDefaultState(String defaultRepositoryUrl){
givenRepositoryDoesNotExist();
givenCreateRepositoryReturnsUrl(defaultRepositoryUrl);
}
public void assertRepositoryNotCreated(){
verify(gitHubServiceMock, never()).createRepository(any(), any());
}
}

View File

@@ -0,0 +1,41 @@
package io.reflectoring.modules.mail;
import io.reflectoring.modules.mail.api.EmailNotificationService;
import org.mockito.Mockito;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Mockito.*;
@TestConfiguration
public class EmailModuleMock {
private final EmailNotificationService emailNotificationServiceMock = Mockito.mock(EmailNotificationService.class);
@Bean
@Primary
EmailNotificationService emailNotificationServiceMock() {
return emailNotificationServiceMock;
}
public void givenSendMailSucceeds() {
// nothing to do, the mock will simply return 0
}
public void givenSendMailThrowsError() {
doThrow(new RuntimeException("error when sending mail"))
.when(emailNotificationServiceMock).sendEmail(anyString(), anyString(), anyString());
}
public void assertSentMailContains(String repositoryUrl) {
verify(emailNotificationServiceMock).sendEmail(anyString(), anyString(), contains(repositoryUrl));
}
public void assertNoMailSent() {
verify(emailNotificationServiceMock, never()).sendEmail(anyString(), anyString(), anyString());
}
}

View File

@@ -0,0 +1,69 @@
package io.reflectoring.modules.rest;
import io.reflectoring.modules.github.GitHubModuleMock;
import io.reflectoring.modules.mail.EmailModuleMock;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest
@TestPropertySource(properties = "mail.enabled=false")
@Import({
GitHubModuleMock.class,
EmailModuleMock.class
})
class RepositoryControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private EmailModuleMock emailModuleMock;
@Autowired
private GitHubModuleMock gitHubModuleMock;
@Test
void givenRepositoryDoesNotExist_thenRepositoryIsCreatedSuccessfully() throws Exception {
String repositoryUrl = "https://github.com/reflectoring/reflectoring.github.io";
gitHubModuleMock.givenDefaultState(repositoryUrl);
emailModuleMock.givenSendMailSucceeds();
mockMvc.perform(post("/github/repository")
.param("token", "123")
.param("repositoryName", "foo")
.param("organizationName", "bar"))
.andExpect(status().is(200));
emailModuleMock.assertSentMailContains(repositoryUrl);
gitHubModuleMock.assertRepositoryCreated();
}
@Test
void givenRepositoryExists_thenReturnsBadRequest() throws Exception {
String repositoryUrl = "https://github.com/reflectoring/reflectoring.github.io";
gitHubModuleMock.givenDefaultState(repositoryUrl);
gitHubModuleMock.givenRepositoryExists();
emailModuleMock.givenSendMailSucceeds();
mockMvc.perform(post("/github/repository")
.param("token", "123")
.param("repositoryName", "foo")
.param("organizationName", "bar"))
.andExpect(status().is(400));
emailModuleMock.assertNoMailSent();
gitHubModuleMock.assertRepositoryNotCreated();
}
}

View File

@@ -0,0 +1,69 @@
package io.reflectoring.modules.rest;
import io.reflectoring.modules.github.api.GitHubMutations;
import io.reflectoring.modules.github.api.GitHubQueries;
import io.reflectoring.modules.mail.api.EmailNotificationService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest
@TestPropertySource(properties = "mail.enabled=false")
class RepositoryControllerTestWithoutModuleMocks {
@Autowired
private MockMvc mockMvc;
@MockBean
private GitHubMutations gitHubMutations;
@MockBean
private GitHubQueries gitHubQueries;
@MockBean
private EmailNotificationService emailNotificationService;
@Test
void givenRepositoryDoesNotExist_thenRepositoryIsCreatedSuccessfully() throws Exception {
String repositoryUrl = "https://github.com/reflectoring/reflectoring.github.io";
given(gitHubQueries.repositoryExists(
anyString(),
anyString(),
anyString())
).willReturn(false);
given(gitHubMutations.createRepository(
any(),
any())
).willReturn(repositoryUrl);
mockMvc.perform(post("/github/repository")
.param("token", "123")
.param("repositoryName", "foo")
.param("organizationName", "bar"))
.andExpect(status().is(200));
verify(emailNotificationService).sendEmail(
anyString(),
anyString(),
contains(repositoryUrl)
);
verify(gitHubMutations).createRepository(
any(),
any()
);
}
}