JAVA-18142 New JHipster-6 module for JDK 11 (#13489)

* JAVA-18142 New JHipster-6 module for JDK 11

* JAVA-18142 Add jhipster-6 to jdk9-and-above

* JAVA-18142 Re-enabled enforcer plugin

* JAVA-18142 Fixed enforcer setup, upgraded spring boot

* JAVA-18142 Try commiting package-lock.json

* JAVA-18142 Removed package-lock.json

* JAVA-18142 Added logs direectory to Git

* JAVA-18142 Tried node npm version and legacy-peer-devs config

* JAVA-18142 Added v for node.version

* JAVA-18142 Corrected a config argument typo

* JAVA-18142 Removed pre-jdk9 profiles

* JAVA-18142 Verify Node/NPM upgrades are needed

* JAVA-18142 Refactored remaining jhipster-5 references to jhipster-6

* JAVA-18142 Fixed more to jhipster6 namespace
This commit is contained in:
Anastasios Ioannidis 2023-04-10 19:37:34 +03:00 committed by GitHub
parent 23c1abe3f2
commit 42dff48c7d
404 changed files with 40007 additions and 0 deletions

3
jhipster-6/README.md Normal file
View File

@ -0,0 +1,3 @@
## JHipster 6
This module contains articles about JHipster 5. This is an aggregator module, articles are in the relevant submodules.

View File

@ -0,0 +1,24 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 4
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[package.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,148 @@
# This file is inspired by https://github.com/alexkaratarakis/gitattributes
#
# Auto detect text files and perform LF normalization
# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
* text=auto
# The above will handle all files NOT found below
# These files are text and should be normalized (Convert crlf => lf)
*.bat text eol=crlf
*.coffee text
*.css text
*.cql text
*.df text
*.ejs text
*.html text
*.java text
*.js text
*.json text
*.less text
*.properties text
*.sass text
*.scss text
*.sh text eol=lf
*.sql text
*.txt text
*.ts text
*.xml text
*.yaml text
*.yml text
# Documents
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.markdown text
*.md text
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
AUTHORS text
CHANGELOG text
CHANGES text
CONTRIBUTING text
COPYING text
copyright text
*COPYRIGHT* text
INSTALL text
license text
LICENSE text
NEWS text
readme text
*README* text
TODO text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default. If you want to treat it as text,
# comment-out the following line and uncomment the line after.
*.svg binary
#*.svg text
*.eps binary
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.class binary
*.jar binary
*.war binary
## LINTERS
.csslintrc text
.eslintrc text
.jscsrc text
.jshintrc text
.jshintignore text
.stylelintrc text
## CONFIGS
*.conf text
*.config text
.editorconfig text
.gitattributes text
.gitconfig text
.gitignore text
.htaccess text
*.npmignore text
## HEROKU
Procfile text
.slugignore text
## AUDIO
*.kar binary
*.m4a binary
*.mid binary
*.midi binary
*.mp3 binary
*.ogg binary
*.ra binary
## VIDEO
*.3gpp binary
*.3gp binary
*.as binary
*.asf binary
*.asx binary
*.fla binary
*.flv binary
*.m4v binary
*.mng binary
*.mov binary
*.mp4 binary
*.mpeg binary
*.mpg binary
*.swc binary
*.swf binary
*.webm binary
## ARCHIVES
*.7z binary
*.gz binary
*.rar binary
*.tar binary
*.zip binary
## FONTS
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary

146
jhipster-6/bookstore-monolith/.gitignore vendored Normal file
View File

@ -0,0 +1,146 @@
######################
# Project Specific
######################
/src/main/webapp/content/css/main.css
/target/www/**
/src/test/javascript/coverage/
######################
# Node
######################
/node/
node_tmp/
node_modules/
npm-debug.log.*
/.awcache/*
/.cache-loader/*
package-lock.json
######################
# SASS
######################
.sass-cache/
######################
# Eclipse
######################
*.pydevproject
.project
.metadata
tmp/
tmp/**/*
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
.factorypath
/src/main/resources/rebel.xml
# External tool builders
.externalToolBuilders/**
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
######################
# Intellij
######################
.idea/
*.iml
*.iws
*.ipr
*.ids
*.orig
classes/
out/
######################
# Visual Studio Code
######################
.vscode/
######################
# Maven
######################
/log/
/target/
######################
# Gradle
######################
.gradle/
/build/
######################
# Package Files
######################
*.jar
*.war
*.ear
*.db
######################
# Windows
######################
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
######################
# Mac OSX
######################
.DS_Store
.svn
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
######################
# Directories
######################
/bin/
/deploy/
######################
# Logs
######################
*.log*
######################
# Others
######################
*.class
*.*~
*~
.merge_file*
######################
# Gradle Wrapper
######################
!gradle/wrapper/gradle-wrapper.jar
######################
# Maven Wrapper
######################
!.mvn/wrapper/maven-wrapper.jar
######################
# ESLint
######################
.eslintcache

View File

@ -0,0 +1,5 @@
{
"hooks": {
"pre-commit": "lint-staged"
}
}

View File

@ -0,0 +1,54 @@
{
"fluentMethods": true,
"clientRootFolder": "",
"relationships": [],
"fields": [
{
"fieldName": "title",
"fieldType": "String",
"fieldValidateRules": [
"required"
]
},
{
"fieldName": "author",
"fieldType": "String",
"fieldValidateRules": [
"required"
]
},
{
"fieldName": "published",
"fieldType": "LocalDate",
"fieldValidateRules": [
"required"
]
},
{
"fieldName": "quantity",
"fieldType": "Integer",
"fieldValidateRules": [
"required",
"min"
],
"fieldValidateRulesMin": 0
},
{
"fieldName": "price",
"fieldType": "Double",
"fieldValidateRules": [
"required",
"min"
],
"fieldValidateRulesMin": 0
}
],
"changelogDate": "20190319124041",
"dto": "mapstruct",
"searchEngine": false,
"service": "serviceImpl",
"entityTableName": "book",
"databaseType": "sql",
"jpaMetamodelFiltering": false,
"pagination": "no"
}

View File

@ -0,0 +1,110 @@
/*
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.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
/**
* 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/0.4.2/maven-wrapper-0.4.2.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 direcrory '" + 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 {
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();
}
}

Binary file not shown.

View File

@ -0,0 +1 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip

View File

@ -0,0 +1,3 @@
node_modules
target
package-lock.json

View File

@ -0,0 +1,12 @@
# Prettier configuration
printWidth: 140
singleQuote: true
tabWidth: 4
useTabs: false
# js and ts rules:
arrowParens: avoid
# jsx and tsx rules:
jsxBracketSameLine: false

View File

@ -0,0 +1,36 @@
{
"generator-jhipster": {
"promptValues": {
"packageName": "com.baeldung.jhipster5"
},
"jhipsterVersion": "6.0.0",
"applicationType": "monolith",
"baseName": "Bookstore",
"packageName": "com.baeldung.jhipster5",
"packageFolder": "com/baeldung/jhipster5",
"serverPort": "8080",
"authenticationType": "jwt",
"cacheProvider": "no",
"websocket": false,
"databaseType": "sql",
"devDatabaseType": "h2Memory",
"prodDatabaseType": "mysql",
"searchEngine": false,
"messageBroker": false,
"serviceDiscoveryType": false,
"buildTool": "maven",
"enableSwaggerCodegen": false,
"jwtSecretKey": "NDJmOTVlZjI2NzhlZDRjNmVkNTM1NDE2NjkyNDljZDJiNzBlMjI5YmZjMjY3MzdjZmZlMjI3NjE4OTRkNzc5MWYzNDNlYWMzYmJjOWRmMjc5ZWQyZTZmOWZkOTMxZWZhNWE1MTVmM2U2NjFmYjhlNDc2Y2Q3NzliMGY0YzFkNmI=",
"clientFramework": "angularX",
"useSass": true,
"clientPackageManager": "npm",
"testFrameworks": [],
"jhiPrefix": "jhi",
"entitySuffix": "",
"dtoSuffix": "DTO",
"otherModules": [],
"enableTranslation": false,
"enableHibernateCache": false,
"clientTheme": "none"
}
}

View File

@ -0,0 +1,5 @@
## Relevant articles:
- [Creating New APIs and Views in JHipster](https://www.baeldung.com/jhipster-new-apis-and-views)
- [JHipster Authentication with an External Service](https://www.baeldung.com/jhipster-authentication-external-service)

View File

@ -0,0 +1,39 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"bookstore": {
"root": "",
"sourceRoot": "src/main/webapp",
"projectType": "application",
"architect": {}
}
},
"defaultProject": "bookstore",
"cli": {
"packageManager": "npm"
},
"schematics": {
"@schematics/angular:component": {
"inlineStyle": true,
"inlineTemplate": false,
"spec": false,
"prefix": "jhi",
"styleExt": "scss"
},
"@schematics/angular:directive": {
"spec": false,
"prefix": "jhi"
},
"@schematics/angular:guard": {
"spec": false
},
"@schematics/angular:pipe": {
"spec": false
},
"@schematics/angular:service": {
"spec": false
}
}
}

286
jhipster-6/bookstore-monolith/mvnw vendored Normal file
View File

@ -0,0 +1,286 @@
#!/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 Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# 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
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
wget "$jarUrl" -O "$wrapperJarPath"
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
curl -o "$wrapperJarPath" "$jarUrl"
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
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 "$@"

161
jhipster-6/bookstore-monolith/mvnw.cmd vendored Normal file
View File

@ -0,0 +1,161 @@
@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 set title of command window
title %0
@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
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.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% (
echo Found %WRAPPER_JAR%
) else (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
echo Finished downloading %WRAPPER_JAR%
)
@REM End of extension
%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,128 @@
{
"name": "bookstore",
"version": "0.0.0",
"description": "Description for Bookstore",
"private": true,
"license": "UNLICENSED",
"cacheDirectories": [
"node_modules"
],
"dependencies": {
"@angular/common": "7.2.4",
"@angular/compiler": "7.2.4",
"@angular/core": "7.2.4",
"@angular/forms": "7.2.4",
"@angular/platform-browser": "7.2.4",
"@angular/platform-browser-dynamic": "7.2.4",
"@angular/router": "7.2.4",
"@fortawesome/angular-fontawesome": "0.3.0",
"@fortawesome/fontawesome-svg-core": "1.2.14",
"@fortawesome/free-solid-svg-icons": "5.7.1",
"@ng-bootstrap/ng-bootstrap": "4.0.2",
"@ngx-translate/core": "11.0.1",
"@ngx-translate/http-loader": "4.0.0",
"bootstrap": "4.2.1",
"core-js": "2.6.4",
"moment": "2.24.0",
"ng-jhipster": "0.9.1",
"ngx-cookie": "2.0.1",
"ngx-infinite-scroll": "7.0.1",
"ngx-webstorage": "2.0.1",
"rxjs": "6.4.0",
"swagger-ui": "2.2.10",
"tslib": "1.9.3",
"zone.js": "0.8.29"
},
"devDependencies": {
"@angular/cli": "7.3.1",
"@angular/compiler-cli": "7.2.4",
"@ngtools/webpack": "7.3.1",
"@types/jest": "24.0.0",
"@types/node": "10.12.24",
"angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2",
"autoprefixer": "9.4.7",
"browser-sync": "2.26.3",
"browser-sync-webpack-plugin": "2.2.2",
"cache-loader": "2.0.1",
"codelyzer": "4.5.0",
"copy-webpack-plugin": "4.6.0",
"css-loader": "2.1.0",
"file-loader": "3.0.1",
"fork-ts-checker-webpack-plugin": "0.5.2",
"friendly-errors-webpack-plugin": "1.7.0",
"generator-jhipster": "6.0.0",
"html-loader": "0.5.5",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"jest": "24.1.0",
"jest-junit": "6.2.1",
"jest-preset-angular": "6.0.2",
"jest-sonar-reporter": "2.0.0",
"lint-staged": "8.1.3",
"mini-css-extract-plugin": "0.5.0",
"moment-locales-webpack-plugin": "1.0.7",
"optimize-css-assets-webpack-plugin": "5.0.1",
"prettier": "1.16.4",
"reflect-metadata": "0.1.13",
"rimraf": "2.6.3",
"simple-progress-webpack-plugin": "1.1.2",
"style-loader": "0.23.1",
"terser-webpack-plugin": "1.2.2",
"thread-loader": "2.1.2",
"to-string-loader": "1.1.5",
"ts-loader": "5.3.3",
"tslint": "5.12.1",
"tslint-config-prettier": "1.18.0",
"tslint-loader": "3.6.0",
"typescript": "3.2.4",
"sass": "1.17.0",
"sass-loader": "7.1.0",
"postcss-loader": "3.0.0",
"xml2js": "0.4.19",
"webpack": "4.29.3",
"webpack-cli": "3.2.3",
"webpack-dev-server": "3.1.14",
"webpack-merge": "4.2.1",
"webpack-notifier": "1.7.0",
"webpack-visualizer-plugin": "0.1.11",
"workbox-webpack-plugin": "3.6.3",
"write-file-webpack-plugin": "4.5.0"
},
"engines": {
"node": ">=8.9.0"
},
"lint-staged": {
"{,src/**/}*.{md,json,ts,css,scss}": [
"prettier --write",
"git add"
]
},
"scripts": {
"prettier:format": "prettier --write \"{,src/**/}*.{md,json,ts,css,scss}\"",
"lint": "tslint --project tsconfig.json -e 'node_modules/**'",
"lint:fix": "npm run lint -- --fix",
"ngc": "ngc -p tsconfig-aot.json",
"cleanup": "rimraf target/{aot,www}",
"clean-www": "rimraf target//www/app/{src,target/}",
"start": "npm run webpack:dev",
"start-tls": "npm run webpack:dev -- --env.tls",
"serve": "npm run start",
"build": "npm run webpack:prod",
"test": "npm run lint && jest --coverage --logHeapUsage -w=2 --config src/test/javascript/jest.conf.js",
"test:watch": "npm run test -- --watch",
"webpack:dev": "npm run webpack-dev-server -- --config webpack/webpack.dev.js --inline --hot --port=9060 --watch-content-base --env.stats=minimal",
"webpack:dev-verbose": "npm run webpack-dev-server -- --config webpack/webpack.dev.js --inline --hot --port=9060 --watch-content-base --profile --progress --env.stats=normal",
"webpack:build:main": "npm run webpack -- --config webpack/webpack.dev.js --env.stats=minimal",
"webpack:build": "npm run cleanup && npm run webpack:build:main",
"webpack:prod:main": "npm run webpack -- --config webpack/webpack.prod.js --profile",
"webpack:prod": "npm run cleanup && npm run webpack:prod:main && npm run clean-www",
"webpack:test": "npm run test",
"webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js",
"webpack": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js"
},
"jestSonar": {
"reportPath": "target/test-results/jest",
"reportFile": "TESTS-results-sonar.xml"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
module.exports = {
plugins: [
require('autoprefixer')
]
}

View File

@ -0,0 +1,7 @@
{
"*": {
"target": "http://localhost:8080",
"secure": false,
"loglevel": "debug"
}
}

View File

@ -0,0 +1,14 @@
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
classes/
generated-sources/
generated-test-sources/
h2db/
maven-archiver/
maven-status/
reports/
surefire-reports/
test-classes/
test-results/
www/
!*.jar
!*.war

View File

@ -0,0 +1,20 @@
FROM openjdk:8-jre-alpine
ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \
JHIPSTER_SLEEP=0 \
JAVA_OPTS=""
# Add a jhipster user to run our application so that it doesn't need to run as root
RUN adduser -D -s /bin/sh jhipster
WORKDIR /home/jhipster
ADD entrypoint.sh entrypoint.sh
RUN chmod 755 entrypoint.sh && chown jhipster:jhipster entrypoint.sh
USER jhipster
ENTRYPOINT ["./entrypoint.sh"]
EXPOSE 8080
ADD *.war app.war

View File

@ -0,0 +1,15 @@
version: '2'
services:
bookstore-app:
image: bookstore
environment:
- _JAVA_OPTIONS=-Xmx512m -Xms256m
- SPRING_PROFILES_ACTIVE=prod,swagger
- SPRING_DATASOURCE_URL=jdbc:mysql://bookstore-mysql:3306/bookstore?useUnicode=true&characterEncoding=utf8&useSSL=false
- JHIPSTER_SLEEP=10 # gives time for the database to boot before the application
ports:
- 8080:8080
bookstore-mysql:
extends:
file: mysql.yml
service: bookstore-mysql

View File

@ -0,0 +1,4 @@
#!/bin/sh
echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP}
exec java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar "${HOME}/app.war" "$@"

View File

@ -0,0 +1,11 @@
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards

View File

@ -0,0 +1,50 @@
apiVersion: 1
# list of datasources that should be deleted from the database
deleteDatasources:
- name: Prometheus
orgId: 1
# list of datasources to insert/update depending
# whats available in the database
datasources:
# <string, required> name of the datasource. Required
- name: Prometheus
# <string, required> datasource type. Required
type: prometheus
# <string, required> access mode. direct or proxy. Required
access: proxy
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
# On MacOS, replace localhost by host.docker.internal
url: http://localhost:9090
# <string> database password, if used
password:
# <string> database user, if used
user:
# <string> database name, if used
database:
# <bool> enable/disable basic auth
basicAuth: false
# <string> basic auth username
basicAuthUser: admin
# <string> basic auth password
basicAuthPassword: admin
# <bool> enable/disable with credentials headers
withCredentials:
# <bool> mark as default datasource. Max one per org
isDefault: true
# <map> fields that will be converted to json and stored in json_data
jsonData:
graphiteVersion: '1.1'
tlsAuth: false
tlsAuthWithCACert: false
# <string> json object of data that will be encrypted.
secureJsonData:
tlsCACert: '...'
tlsClientCert: '...'
tlsClientKey: '...'
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

View File

@ -0,0 +1,26 @@
version: '3'
services:
bookstore-prometheus:
image: prom/prometheus:v2.9.2
volumes:
- ./prometheus/:/etc/prometheus/
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- 9090:9090
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
# grafana/provisioning/datasources/datasource.yml
network_mode: 'host' # to test locally running service
bookstore-grafana:
image: grafana/grafana:6.1.4
volumes:
- ./grafana/provisioning/:/etc/grafana/provisioning/
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
- GF_INSTALL_PLUGINS=grafana-piechart-panel
ports:
- 3000:3000
# On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
# grafana/provisioning/datasources/datasource.yml
network_mode: 'host' # to test locally running service

View File

@ -0,0 +1,13 @@
version: '2'
services:
bookstore-mysql:
image: mysql:5.7.20
# volumes:
# - ~/volumes/jhipster/Bookstore/mysql/:/var/lib/mysql/
environment:
- MYSQL_USER=root
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
- MYSQL_DATABASE=bookstore
ports:
- 3306:3306
command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8mb4 --explicit_defaults_for_timestamp

View File

@ -0,0 +1,26 @@
# Sample global config for monitoring JHipster applications
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
evaluation_interval: 15s # By default, scrape targets every 15 seconds.
# scrape_timeout is set to the global default (10s).
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'jhipster'
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
# scheme defaults to 'http'.
metrics_path: /management/prometheus
static_configs:
- targets:
# On MacOS, replace localhost by host.docker.internal
- localhost:8080

View File

@ -0,0 +1,7 @@
version: '2'
services:
bookstore-sonar:
image: sonarqube:7.1
ports:
- 9001:9000
- 9092:9092

View File

@ -0,0 +1,21 @@
package com.baeldung.jhipster6;
import com.baeldung.jhipster6.config.DefaultProfileUtil;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* This is a helper Java class that provides an alternative to creating a web.xml.
* This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc.
*/
public class ApplicationWebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
/**
* set a default to use when no profile is configured.
*/
DefaultProfileUtil.addDefaultProfile(application.application());
return application.sources(BookstoreApp.class);
}
}

View File

@ -0,0 +1,98 @@
package com.baeldung.jhipster6;
import com.baeldung.jhipster6.config.ApplicationProperties;
import com.baeldung.jhipster6.config.DefaultProfileUtil;
import io.github.jhipster.config.JHipsterConstants;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
@SpringBootApplication
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
public class BookstoreApp {
private static final Logger log = LoggerFactory.getLogger(BookstoreApp.class);
private final Environment env;
public BookstoreApp(Environment env) {
this.env = env;
}
/**
* Initializes Bookstore.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on <a href="https://www.jhipster.tech/profiles/">https://www.jhipster.tech/profiles/</a>.
*/
@PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(BookstoreApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
String serverPort = env.getProperty("server.port");
String contextPath = env.getProperty("server.servlet.context-path");
if (StringUtils.isBlank(contextPath)) {
contextPath = "/";
}
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}{}\n\t" +
"External: \t{}://{}:{}{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles());
}
}

View File

@ -0,0 +1,98 @@
package com.baeldung.jhipster6.aop.logging;
import io.github.jhipster.config.JHipsterConstants;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.util.Arrays;
/**
* Aspect for logging execution of service and repository Spring components.
*
* By default, it only runs with the "dev" profile.
*/
@Aspect
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final Environment env;
public LoggingAspect(Environment env) {
this.env = env;
}
/**
* Pointcut that matches all repositories, services and Web REST endpoints.
*/
@Pointcut("within(@org.springframework.stereotype.Repository *)" +
" || within(@org.springframework.stereotype.Service *)" +
" || within(@org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
@Pointcut("within(com.baeldung.jhipster5.repository..*)"+
" || within(com.baeldung.jhipster5.service..*)"+
" || within(com.baeldung.jhipster5.web.rest..*)")
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
/**
* Advice that logs methods throwing exceptions.
*
* @param joinPoint join point for advice
* @param e exception
*/
@AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
log.error("Exception in {}.{}() with cause = \'{}\' and exception = \'{}\'", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL", e.getMessage(), e);
} else {
log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), e.getCause() != null? e.getCause() : "NULL");
}
}
/**
* Advice that logs when a method is entered and exited.
*
* @param joinPoint join point for advice
* @return result
* @throws Throwable throws IllegalArgumentException
*/
@Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
}
try {
Object result = joinPoint.proceed();
if (log.isDebugEnabled()) {
log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(), result);
}
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
throw e;
}
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.jhipster6.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties specific to Bookstore.
* <p>
* Properties are configured in the application.yml file.
* See {@link io.github.jhipster.config.JHipsterProperties} for a good example.
*/
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
}

View File

@ -0,0 +1,59 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.async.ExceptionHandlingAsyncTaskExecutor;
import io.github.jhipster.config.JHipsterProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer, SchedulingConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final JHipsterProperties jHipsterProperties;
public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
executor.setThreadNamePrefix("bookstore-Executor-");
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(scheduledTaskExecutor());
}
@Bean
public Executor scheduledTaskExecutor() {
return Executors.newScheduledThreadPool(jHipsterProperties.getAsync().getCorePoolSize());
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.config.JHipsterConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Configuration
@Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
private static final String CLOUD_CONFIGURATION_HIKARI_PREFIX = "spring.datasource.hikari";
@Bean
@ConfigurationProperties(CLOUD_CONFIGURATION_HIKARI_PREFIX)
public DataSource dataSource() {
log.info("Configuring JDBC datasource from a cloud provider");
return connectionFactory().dataSource();
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.jhipster6.config;
/**
* Application constants.
*/
public final class Constants {
// Regex for acceptable logins
public static final String LOGIN_REGEX = "^[_.@A-Za-z0-9-]*$";
public static final String SYSTEM_ACCOUNT = "system";
public static final String ANONYMOUS_USER = "anonymoususer";
public static final String DEFAULT_LANGUAGE = "en";
private Constants() {
}
}

View File

@ -0,0 +1,59 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.h2.H2ConfigurationHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.sql.SQLException;
@Configuration
@EnableJpaRepositories("com.baeldung.jhipster5.repository")
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
private final Environment env;
public DatabaseConfiguration(Environment env) {
this.env = env;
}
/**
* Open the TCP port for the H2 database, so it is available remotely.
*
* @return the H2 database TCP server
* @throws SQLException if the server failed to start
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public Object h2TCPServer() throws SQLException {
String port = getValidPortForH2();
log.debug("H2 database is available on port {}", port);
return H2ConfigurationHelper.createServer(port);
}
private String getValidPortForH2() {
int port = Integer.parseInt(env.getProperty("server.port"));
if (port < 10000) {
port = 10000 + port;
} else {
if (port < 63536) {
port = port + 2000;
} else {
port = port - 2000;
}
}
return String.valueOf(port);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.jhipster6.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Configure the converters to use the ISO format for dates by default.
*/
@Configuration
public class DateTimeFormatConfiguration implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}

View File

@ -0,0 +1,51 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.config.JHipsterConstants;
import org.springframework.boot.SpringApplication;
import org.springframework.core.env.Environment;
import java.util.*;
/**
* Utility class to load a Spring profile to be used as default
* when there is no <code>spring.profiles.active</code> set in the environment or as command line argument.
* If the value is not available in <code>application.yml</code> then <code>dev</code> profile will be used as default.
*/
public final class DefaultProfileUtil {
private static final String SPRING_PROFILE_DEFAULT = "spring.profiles.default";
private DefaultProfileUtil() {
}
/**
* Set a default to use when no profile is configured.
*
* @param app the Spring application
*/
public static void addDefaultProfile(SpringApplication app) {
Map<String, Object> defProperties = new HashMap<>();
/*
* The default profile to use when no other profiles are defined
* This cannot be set in the <code>application.yml</code> file.
* See https://github.com/spring-projects/spring-boot/issues/1219
*/
defProperties.put(SPRING_PROFILE_DEFAULT, JHipsterConstants.SPRING_PROFILE_DEVELOPMENT);
app.setDefaultProperties(defProperties);
}
/**
* Get the profiles that are applied else get default profiles.
*
* @param env spring environment
* @return profiles
*/
public static String[] getActiveProfiles(Environment env) {
String[] profiles = env.getActiveProfiles();
if (profiles.length == 0) {
return env.getDefaultProfiles();
}
return profiles;
}
}

View File

@ -0,0 +1,63 @@
package com.baeldung.jhipster6.config;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.zalando.problem.ProblemModule;
import org.zalando.problem.violations.ConstraintViolationProblemModule;
@Configuration
public class JacksonConfiguration {
/**
* Support for Java date and time API.
* @return the corresponding Jackson module.
*/
@Bean
public JavaTimeModule javaTimeModule() {
return new JavaTimeModule();
}
@Bean
public Jdk8Module jdk8TimeModule() {
return new Jdk8Module();
}
/*
* Support for Hibernate types in Jackson.
*/
@Bean
public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
/*
* Jackson Afterburner module to speed up serialization/deserialization.
*/
@Bean
public AfterburnerModule afterburnerModule() {
return new AfterburnerModule();
}
/*
* Module for serialization/deserialization of RFC7807 Problem.
*/
@Bean
ProblemModule problemModule() {
return new ProblemModule();
}
/*
* Module for serialization/deserialization of ConstraintViolationProblem.
*/
@Bean
ConstraintViolationProblemModule constraintViolationProblemModule() {
return new ConstraintViolationProblemModule();
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.jhipster6.config;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.liquibase.AsyncSpringLiquibase;
import liquibase.integration.spring.SpringLiquibase;
@Configuration
public class LiquibaseConfiguration {
private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class);
private final Environment env;
public LiquibaseConfiguration(Environment env) {
this.env = env;
}
@Bean
public SpringLiquibase liquibase(@Qualifier("taskExecutor") TaskExecutor taskExecutor,
DataSource dataSource, LiquibaseProperties liquibaseProperties) {
// Use liquibase.integration.spring.SpringLiquibase if you don't want Liquibase to start asynchronously
SpringLiquibase liquibase = new AsyncSpringLiquibase(taskExecutor, env);
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setChangeLogParameters(liquibaseProperties.getParameters());
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE)) {
liquibase.setShouldRun(false);
} else {
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase");
}
return liquibase;
}
}

View File

@ -0,0 +1,27 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.config.locale.AngularCookieLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
@Configuration
public class LocaleConfiguration implements WebMvcConfigurer {
@Bean(name = "localeResolver")
public LocaleResolver localeResolver() {
AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
return cookieLocaleResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
registry.addInterceptor(localeChangeInterceptor);
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.jhipster6.config;
import com.baeldung.jhipster6.aop.logging.LoggingAspect;
import io.github.jhipster.config.JHipsterConstants;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Configuration
@EnableAspectJAutoProxy
public class LoggingAspectConfiguration {
@Bean
@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
public LoggingAspect loggingAspect(Environment env) {
return new LoggingAspect(env);
}
}

View File

@ -0,0 +1,154 @@
package com.baeldung.jhipster6.config;
import java.net.InetSocketAddress;
import java.util.Iterator;
import io.github.jhipster.config.JHipsterProperties;
import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.boolex.OnMarkerEvaluator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.filter.EvaluatorFilter;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.FilterReply;
import net.logstash.logback.appender.LogstashTcpSocketAppender;
import net.logstash.logback.encoder.LogstashEncoder;
import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoggingConfiguration {
private static final String LOGSTASH_APPENDER_NAME = "LOGSTASH";
private static final String ASYNC_LOGSTASH_APPENDER_NAME = "ASYNC_LOGSTASH";
private final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
private final String appName;
private final String serverPort;
private final JHipsterProperties jHipsterProperties;
public LoggingConfiguration(@Value("${spring.application.name}") String appName, @Value("${server.port}") String serverPort,
JHipsterProperties jHipsterProperties) {
this.appName = appName;
this.serverPort = serverPort;
this.jHipsterProperties = jHipsterProperties;
if (jHipsterProperties.getLogging().getLogstash().isEnabled()) {
addLogstashAppender(context);
addContextListener(context);
}
if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
setMetricsMarkerLogbackFilter(context);
}
}
private void addContextListener(LoggerContext context) {
LogbackLoggerContextListener loggerContextListener = new LogbackLoggerContextListener();
loggerContextListener.setContext(context);
context.addListener(loggerContextListener);
}
private void addLogstashAppender(LoggerContext context) {
log.info("Initializing Logstash logging");
LogstashTcpSocketAppender logstashAppender = new LogstashTcpSocketAppender();
logstashAppender.setName(LOGSTASH_APPENDER_NAME);
logstashAppender.setContext(context);
String customFields = "{\"app_name\":\"" + appName + "\",\"app_port\":\"" + serverPort + "\"}";
// More documentation is available at: https://github.com/logstash/logstash-logback-encoder
LogstashEncoder logstashEncoder = new LogstashEncoder();
// Set the Logstash appender config from JHipster properties
logstashAppender.addDestinations(new InetSocketAddress(jHipsterProperties.getLogging().getLogstash().getHost(), jHipsterProperties.getLogging().getLogstash().getPort()));
ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
throwableConverter.setRootCauseFirst(true);
logstashEncoder.setThrowableConverter(throwableConverter);
logstashEncoder.setCustomFields(customFields);
logstashAppender.setEncoder(logstashEncoder);
logstashAppender.start();
// Wrap the appender in an Async appender for performance
AsyncAppender asyncLogstashAppender = new AsyncAppender();
asyncLogstashAppender.setContext(context);
asyncLogstashAppender.setName(ASYNC_LOGSTASH_APPENDER_NAME);
asyncLogstashAppender.setQueueSize(jHipsterProperties.getLogging().getLogstash().getQueueSize());
asyncLogstashAppender.addAppender(logstashAppender);
asyncLogstashAppender.start();
context.getLogger("ROOT").addAppender(asyncLogstashAppender);
}
// Configure a log filter to remove "metrics" logs from all appenders except the "LOGSTASH" appender
private void setMetricsMarkerLogbackFilter(LoggerContext context) {
log.info("Filtering metrics logs from all appenders except the {} appender", LOGSTASH_APPENDER_NAME);
OnMarkerEvaluator onMarkerMetricsEvaluator = new OnMarkerEvaluator();
onMarkerMetricsEvaluator.setContext(context);
onMarkerMetricsEvaluator.addMarker("metrics");
onMarkerMetricsEvaluator.start();
EvaluatorFilter<ILoggingEvent> metricsFilter = new EvaluatorFilter<>();
metricsFilter.setContext(context);
metricsFilter.setEvaluator(onMarkerMetricsEvaluator);
metricsFilter.setOnMatch(FilterReply.DENY);
metricsFilter.start();
for (ch.qos.logback.classic.Logger logger : context.getLoggerList()) {
for (Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders(); it.hasNext();) {
Appender<ILoggingEvent> appender = it.next();
if (!appender.getName().equals(ASYNC_LOGSTASH_APPENDER_NAME)) {
log.debug("Filter metrics logs from the {} appender", appender.getName());
appender.setContext(context);
appender.addFilter(metricsFilter);
appender.start();
}
}
}
}
/**
* Logback configuration is achieved by configuration file and API.
* When configuration file change is detected, the configuration is reset.
* This listener ensures that the programmatic configuration is also re-applied after reset.
*/
class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {
@Override
public boolean isResetResistant() {
return true;
}
@Override
public void onStart(LoggerContext context) {
addLogstashAppender(context);
}
@Override
public void onReset(LoggerContext context) {
addLogstashAppender(context);
}
@Override
public void onStop(LoggerContext context) {
// Nothing to do.
}
@Override
public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
// Nothing to do.
}
}
}

View File

@ -0,0 +1,125 @@
package com.baeldung.jhipster6.config;
import com.baeldung.jhipster6.security.*;
import com.baeldung.jhipster6.security.jwt.*;
import com.baeldung.jhipster6.security.AuthoritiesConstants;
import com.baeldung.jhipster6.security.jwt.JWTConfigurer;
import com.baeldung.jhipster6.security.jwt.TokenProvider;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
import javax.annotation.PostConstruct;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final TokenProvider tokenProvider;
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService, TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.userDetailsService = userDetailsService;
this.tokenProvider = tokenProvider;
this.corsFilter = corsFilter;
this.problemSupport = problemSupport;
}
@PostConstruct
public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} catch (Exception e) {
throw new BeanInitializationException("Security configuration failed", e);
}
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/h2-console/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/books/purchase/**").authenticated()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/account/reset-password/init").permitAll()
.antMatchers("/api/account/reset-password/finish").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/info").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.and()
.apply(securityConfigurerAdapter());
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}

View File

@ -0,0 +1,142 @@
package com.baeldung.jhipster6.config;
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.JHipsterProperties;
import io.github.jhipster.web.filter.CachingHttpHeadersFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.server.*;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.http.MediaType;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import javax.servlet.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.*;
import static java.net.URLDecoder.decode;
/**
* Configuration of web application with Servlet 3.0 APIs.
*/
@Configuration
public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer<WebServerFactory> {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_PRODUCTION))) {
initCachingHttpHeadersFilter(servletContext, disps);
}
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
@Override
public void customize(WebServerFactory server) {
setMimeMappings(server);
// When running in an IDE or with ./mvnw spring-boot:run, set location of the static web assets.
setLocationForStaticAssets(server);
}
private void setMimeMappings(WebServerFactory server) {
if (server instanceof ConfigurableServletWebServerFactory) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
// IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server;
servletWebServer.setMimeMappings(mappings);
}
}
private void setLocationForStaticAssets(WebServerFactory server) {
if (server instanceof ConfigurableServletWebServerFactory) {
ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server;
File root;
String prefixPath = resolvePathPrefix();
root = new File(prefixPath + "target/classes/static/");
if (root.exists() && root.isDirectory()) {
servletWebServer.setDocumentRoot(root);
}
}
}
/**
* Resolve path prefix to static resources.
*/
private String resolvePathPrefix() {
String fullExecutablePath;
try {
fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
/* try without decoding if this ever happens */
fullExecutablePath = this.getClass().getResource("").getPath();
}
String rootPath = Paths.get(".").toUri().normalize().getPath();
String extractedPath = fullExecutablePath.replace(rootPath, "");
int extractionEndIndex = extractedPath.indexOf("target/");
if (extractionEndIndex <= 0) {
return "";
}
return extractedPath.substring(0, extractionEndIndex);
}
/**
* Initializes the caching HTTP Headers Filter.
*/
private void initCachingHttpHeadersFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering Caching HTTP Headers Filter");
FilterRegistration.Dynamic cachingHttpHeadersFilter =
servletContext.addFilter("cachingHttpHeadersFilter",
new CachingHttpHeadersFilter(jHipsterProperties));
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/i18n/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
cachingHttpHeadersFilter.setAsyncSupported(true);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/management/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
}
}

View File

@ -0,0 +1,86 @@
package com.baeldung.jhipster6.config.audit;
import com.baeldung.jhipster6.domain.PersistentAuditEvent;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class AuditEventConverter {
/**
* Convert a list of PersistentAuditEvent to a list of AuditEvent
*
* @param persistentAuditEvents the list to convert
* @return the converted list.
*/
public List<AuditEvent> convertToAuditEvent(Iterable<PersistentAuditEvent> persistentAuditEvents) {
if (persistentAuditEvents == null) {
return Collections.emptyList();
}
List<AuditEvent> auditEvents = new ArrayList<>();
for (PersistentAuditEvent persistentAuditEvent : persistentAuditEvents) {
auditEvents.add(convertToAuditEvent(persistentAuditEvent));
}
return auditEvents;
}
/**
* Convert a PersistentAuditEvent to an AuditEvent
*
* @param persistentAuditEvent the event to convert
* @return the converted list.
*/
public AuditEvent convertToAuditEvent(PersistentAuditEvent persistentAuditEvent) {
if (persistentAuditEvent == null) {
return null;
}
return new AuditEvent(persistentAuditEvent.getAuditEventDate(), persistentAuditEvent.getPrincipal(),
persistentAuditEvent.getAuditEventType(), convertDataToObjects(persistentAuditEvent.getData()));
}
/**
* Internal conversion. This is needed to support the current SpringBoot actuator AuditEventRepository interface
*
* @param data the data to convert
* @return a map of String, Object
*/
public Map<String, Object> convertDataToObjects(Map<String, String> data) {
Map<String, Object> results = new HashMap<>();
if (data != null) {
for (Map.Entry<String, String> entry : data.entrySet()) {
results.put(entry.getKey(), entry.getValue());
}
}
return results;
}
/**
* Internal conversion. This method will allow to save additional data.
* By default, it will save the object as string
*
* @param data the data to convert
* @return a map of String, String
*/
public Map<String, String> convertDataToStrings(Map<String, Object> data) {
Map<String, String> results = new HashMap<>();
if (data != null) {
for (Map.Entry<String, Object> entry : data.entrySet()) {
// Extract the data that will be saved.
if (entry.getValue() instanceof WebAuthenticationDetails) {
WebAuthenticationDetails authenticationDetails = (WebAuthenticationDetails) entry.getValue();
results.put("remoteAddress", authenticationDetails.getRemoteAddress());
results.put("sessionId", authenticationDetails.getSessionId());
} else {
results.put(entry.getKey(), Objects.toString(entry.getValue()));
}
}
}
return results;
}
}

View File

@ -0,0 +1,4 @@
/**
* Audit specific code.
*/
package com.baeldung.jhipster6.config.audit;

View File

@ -0,0 +1,4 @@
/**
* Spring Framework configuration files.
*/
package com.baeldung.jhipster6.config;

View File

@ -0,0 +1,79 @@
package com.baeldung.jhipster6.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.envers.Audited;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.io.Serializable;
import java.time.Instant;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
/**
* Base abstract class for entities which will hold definitions for created, last modified by and created,
* last modified by date.
*/
@MappedSuperclass
@Audited
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@CreatedBy
@Column(name = "created_by", nullable = false, length = 50, updatable = false)
@JsonIgnore
private String createdBy;
@CreatedDate
@Column(name = "created_date", updatable = false)
@JsonIgnore
private Instant createdDate = Instant.now();
@LastModifiedBy
@Column(name = "last_modified_by", length = 50)
@JsonIgnore
private String lastModifiedBy;
@LastModifiedDate
@Column(name = "last_modified_date")
@JsonIgnore
private Instant lastModifiedDate = Instant.now();
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Instant getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Instant createdDate) {
this.createdDate = createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public Instant getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Instant lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
}

View File

@ -0,0 +1,59 @@
package com.baeldung.jhipster6.domain;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
/**
* An authority (a security role) used by Spring Security.
*/
@Entity
@Table(name = "jhi_authority")
public class Authority implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull
@Size(max = 50)
@Id
@Column(length = 50)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Authority authority = (Authority) o;
return !(name != null ? !name.equals(authority.name) : authority.name != null);
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
@Override
public String toString() {
return "Authority{" +
"name='" + name + '\'' +
"}";
}
}

View File

@ -0,0 +1,153 @@
package com.baeldung.jhipster6.domain;
import javax.persistence.*;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;
/**
* A Book.
*/
@Entity
@Table(name = "book")
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Column(name = "title", nullable = false)
private String title;
@NotNull
@Column(name = "author", nullable = false)
private String author;
@NotNull
@Column(name = "published", nullable = false)
private LocalDate published;
@NotNull
@Min(value = 0)
@Column(name = "quantity", nullable = false)
private Integer quantity;
@NotNull
@DecimalMin(value = "0")
@Column(name = "price", nullable = false)
private Double price;
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public Book title(String title) {
this.title = title;
return this;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public Book author(String author) {
this.author = author;
return this;
}
public void setAuthor(String author) {
this.author = author;
}
public LocalDate getPublished() {
return published;
}
public Book published(LocalDate published) {
this.published = published;
return this;
}
public void setPublished(LocalDate published) {
this.published = published;
}
public Integer getQuantity() {
return quantity;
}
public Book quantity(Integer quantity) {
this.quantity = quantity;
return this;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Double getPrice() {
return price;
}
public Book price(Double price) {
this.price = price;
return this;
}
public void setPrice(Double price) {
this.price = price;
}
// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Book book = (Book) o;
if (book.getId() == null || getId() == null) {
return false;
}
return Objects.equals(getId(), book.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
@Override
public String toString() {
return "Book{" +
"id=" + getId() +
", title='" + getTitle() + "'" +
", author='" + getAuthor() + "'" +
", published='" + getPublished() + "'" +
", quantity=" + getQuantity() +
", price=" + getPrice() +
"}";
}
}

View File

@ -0,0 +1,109 @@
package com.baeldung.jhipster6.domain;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.Instant;
import java.util.HashMap;
import java.util.Objects;
import java.util.Map;
/**
* Persist AuditEvent managed by the Spring Boot actuator.
*
* @see org.springframework.boot.actuate.audit.AuditEvent
*/
@Entity
@Table(name = "jhi_persistent_audit_event")
public class PersistentAuditEvent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "event_id")
private Long id;
@NotNull
@Column(nullable = false)
private String principal;
@Column(name = "event_date")
private Instant auditEventDate;
@Column(name = "event_type")
private String auditEventType;
@ElementCollection
@MapKeyColumn(name = "name")
@Column(name = "value")
@CollectionTable(name = "jhi_persistent_audit_evt_data", joinColumns=@JoinColumn(name="event_id"))
private Map<String, String> data = new HashMap<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPrincipal() {
return principal;
}
public void setPrincipal(String principal) {
this.principal = principal;
}
public Instant getAuditEventDate() {
return auditEventDate;
}
public void setAuditEventDate(Instant auditEventDate) {
this.auditEventDate = auditEventDate;
}
public String getAuditEventType() {
return auditEventType;
}
public void setAuditEventType(String auditEventType) {
this.auditEventType = auditEventType;
}
public Map<String, String> getData() {
return data;
}
public void setData(Map<String, String> data) {
this.data = data;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PersistentAuditEvent persistentAuditEvent = (PersistentAuditEvent) o;
return !(persistentAuditEvent.getId() == null || getId() == null) && Objects.equals(getId(), persistentAuditEvent.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
@Override
public String toString() {
return "PersistentAuditEvent{" +
"principal='" + principal + '\'' +
", auditEventDate=" + auditEventDate +
", auditEventType='" + auditEventType + '\'' +
'}';
}
}

View File

@ -0,0 +1,231 @@
package com.baeldung.jhipster6.domain;
import com.baeldung.jhipster6.config.Constants;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.BatchSize;
import javax.validation.constraints.Email;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.time.Instant;
/**
* A user.
*/
@Entity
@Table(name = "jhi_user")
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
@Column(length = 50, unique = true, nullable = false)
private String login;
@JsonIgnore
@NotNull
@Size(min = 60, max = 60)
@Column(name = "password_hash", length = 60, nullable = false)
private String password;
@Size(max = 50)
@Column(name = "first_name", length = 50)
private String firstName;
@Size(max = 50)
@Column(name = "last_name", length = 50)
private String lastName;
@Email
@Size(min = 5, max = 254)
@Column(length = 254, unique = true)
private String email;
@NotNull
@Column(nullable = false)
private boolean activated = false;
@Size(min = 2, max = 6)
@Column(name = "lang_key", length = 6)
private String langKey;
@Size(max = 256)
@Column(name = "image_url", length = 256)
private String imageUrl;
@Size(max = 20)
@Column(name = "activation_key", length = 20)
@JsonIgnore
private String activationKey;
@Size(max = 20)
@Column(name = "reset_key", length = 20)
@JsonIgnore
private String resetKey;
@Column(name = "reset_date")
private Instant resetDate = null;
@JsonIgnore
@ManyToMany
@JoinTable(
name = "jhi_user_authority",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")})
@BatchSize(size = 20)
private Set<Authority> authorities = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLogin() {
return login;
}
// Lowercase the login before saving it in database
public void setLogin(String login) {
this.login = StringUtils.lowerCase(login, Locale.ENGLISH);
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public boolean getActivated() {
return activated;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
public String getActivationKey() {
return activationKey;
}
public void setActivationKey(String activationKey) {
this.activationKey = activationKey;
}
public String getResetKey() {
return resetKey;
}
public void setResetKey(String resetKey) {
this.resetKey = resetKey;
}
public Instant getResetDate() {
return resetDate;
}
public void setResetDate(Instant resetDate) {
this.resetDate = resetDate;
}
public String getLangKey() {
return langKey;
}
public void setLangKey(String langKey) {
this.langKey = langKey;
}
public Set<Authority> getAuthorities() {
return authorities;
}
public void setAuthorities(Set<Authority> authorities) {
this.authorities = authorities;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return !(user.getId() == null || getId() == null) && Objects.equals(getId(), user.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
@Override
public String toString() {
return "User{" +
"login='" + login + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", imageUrl='" + imageUrl + '\'' +
", activated='" + activated + '\'' +
", langKey='" + langKey + '\'' +
", activationKey='" + activationKey + '\'' +
"}";
}
}

View File

@ -0,0 +1,4 @@
/**
* JPA domain objects.
*/
package com.baeldung.jhipster6.domain;

View File

@ -0,0 +1,11 @@
package com.baeldung.jhipster6.repository;
import com.baeldung.jhipster6.domain.Authority;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Spring Data JPA repository for the Authority entity.
*/
public interface AuthorityRepository extends JpaRepository<Authority, String> {
}

View File

@ -0,0 +1,15 @@
package com.baeldung.jhipster6.repository;
import com.baeldung.jhipster6.domain.Book;
import org.springframework.data.jpa.repository.*;
import org.springframework.stereotype.Repository;
/**
* Spring Data repository for the Book entity.
*/
@SuppressWarnings("unused")
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}

View File

@ -0,0 +1,89 @@
package com.baeldung.jhipster6.repository;
import com.baeldung.jhipster6.config.Constants;
import com.baeldung.jhipster6.config.audit.AuditEventConverter;
import com.baeldung.jhipster6.domain.PersistentAuditEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.*;
/**
* An implementation of Spring Boot's AuditEventRepository.
*/
@Repository
public class CustomAuditEventRepository implements AuditEventRepository {
private static final String AUTHORIZATION_FAILURE = "AUTHORIZATION_FAILURE";
/**
* Should be the same as in Liquibase migration.
*/
protected static final int EVENT_DATA_COLUMN_MAX_LENGTH = 255;
private final PersistenceAuditEventRepository persistenceAuditEventRepository;
private final AuditEventConverter auditEventConverter;
private final Logger log = LoggerFactory.getLogger(getClass());
public CustomAuditEventRepository(PersistenceAuditEventRepository persistenceAuditEventRepository,
AuditEventConverter auditEventConverter) {
this.persistenceAuditEventRepository = persistenceAuditEventRepository;
this.auditEventConverter = auditEventConverter;
}
@Override
public List<AuditEvent> find(String principal, Instant after, String type) {
Iterable<PersistentAuditEvent> persistentAuditEvents =
persistenceAuditEventRepository.findByPrincipalAndAuditEventDateAfterAndAuditEventType(principal, after, type);
return auditEventConverter.convertToAuditEvent(persistentAuditEvents);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void add(AuditEvent event) {
if (!AUTHORIZATION_FAILURE.equals(event.getType()) &&
!Constants.ANONYMOUS_USER.equals(event.getPrincipal())) {
PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
persistentAuditEvent.setPrincipal(event.getPrincipal());
persistentAuditEvent.setAuditEventType(event.getType());
persistentAuditEvent.setAuditEventDate(event.getTimestamp());
Map<String, String> eventData = auditEventConverter.convertDataToStrings(event.getData());
persistentAuditEvent.setData(truncate(eventData));
persistenceAuditEventRepository.save(persistentAuditEvent);
}
}
/**
* Truncate event data that might exceed column length.
*/
private Map<String, String> truncate(Map<String, String> data) {
Map<String, String> results = new HashMap<>();
if (data != null) {
for (Map.Entry<String, String> entry : data.entrySet()) {
String value = entry.getValue();
if (value != null) {
int length = value.length();
if (length > EVENT_DATA_COLUMN_MAX_LENGTH) {
value = value.substring(0, EVENT_DATA_COLUMN_MAX_LENGTH);
log.warn("Event data for {} too long ({}) has been truncated to {}. Consider increasing column width.",
entry.getKey(), length, EVENT_DATA_COLUMN_MAX_LENGTH);
}
}
results.put(entry.getKey(), value);
}
}
return results;
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.jhipster6.repository;
import com.baeldung.jhipster6.domain.PersistentAuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.Instant;
import java.util.List;
/**
* Spring Data JPA repository for the PersistentAuditEvent entity.
*/
public interface PersistenceAuditEventRepository extends JpaRepository<PersistentAuditEvent, Long> {
List<PersistentAuditEvent> findByPrincipal(String principal);
List<PersistentAuditEvent> findByAuditEventDateAfter(Instant after);
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfter(String principal, Instant after);
List<PersistentAuditEvent> findByPrincipalAndAuditEventDateAfterAndAuditEventType(String principal, Instant after, String type);
Page<PersistentAuditEvent> findAllByAuditEventDateBetween(Instant fromDate, Instant toDate, Pageable pageable);
}

View File

@ -0,0 +1,40 @@
package com.baeldung.jhipster6.repository;
import com.baeldung.jhipster6.domain.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.time.Instant;
/**
* Spring Data JPA repository for the User entity.
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findOneByActivationKey(String activationKey);
List<User> findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime);
Optional<User> findOneByResetKey(String resetKey);
Optional<User> findOneByEmailIgnoreCase(String email);
Optional<User> findOneByLogin(String login);
@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesById(Long id);
@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesByLogin(String login);
@EntityGraph(attributePaths = "authorities")
Optional<User> findOneWithAuthoritiesByEmail(String email);
Page<User> findAllByLoginNot(Pageable pageable, String login);
}

View File

@ -0,0 +1,4 @@
/**
* Spring Data JPA repositories.
*/
package com.baeldung.jhipster6.repository;

View File

@ -0,0 +1,16 @@
package com.baeldung.jhipster6.security;
/**
* Constants for Spring Security authorities.
*/
public final class AuthoritiesConstants {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
private AuthoritiesConstants() {
}
}

View File

@ -0,0 +1,126 @@
package com.baeldung.jhipster6.security;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.security.dto.LoginRequest;
import com.baeldung.jhipster6.security.dto.LoginResponse;
import com.baeldung.jhipster6.service.UserService;
import com.baeldung.jhipster6.service.dto.UserDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
@Component
public class CustomAuthenticationManager implements AuthenticationManager {
private final static Logger LOG = LoggerFactory.getLogger(CustomAuthenticationManager.class);
private final String REMOTE_LOGIN_URL = "https://example.com/login";
private final RestTemplate restTemplate = new RestTemplate();
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LoginRequest loginRequest = new LoginRequest();
loginRequest.setUsername(authentication.getPrincipal().toString());
loginRequest.setPassword(authentication.getCredentials().toString());
try
{
ResponseEntity<LoginResponse> response =
restTemplate.postForEntity(
REMOTE_LOGIN_URL,
loginRequest,
LoginResponse.class);
if(response.getStatusCode().is2xxSuccessful())
{
//
// Need to create a new local user if this is the first time logging in; this
// is required so they can be issued JWTs. We can use this flow to also keep
// our local use entry up to date with data from the remote service if needed
// (for example, if the first and last name might change, this is where we would
// update the local user entry)
//
User user = userService.getUserWithAuthoritiesByLogin(authentication.getPrincipal().toString())
.orElseGet(() -> userService.createUser(createUserDTO(response.getBody(), authentication)));
return createAuthentication(authentication, user);
}
else
{
throw new BadCredentialsException("Invalid username or password");
}
}
catch (Exception e)
{
LOG.warn("Failed to authenticate", e);
throw new AuthenticationServiceException("Failed to login", e);
}
}
/**
* Creates a new authentication with basic roles
* @param auth Contains auth details that will be copied into the new one.
* @param user User object representing who is logging in
* @return Authentication
*/
private Authentication createAuthentication(Authentication auth, User user) {
//
// Honor any roles the user already has set; default is just USER role
// but could be modified after account creation
//
Collection<? extends GrantedAuthority> authorities = user
.getAuthorities()
.stream()
.map(a -> new SimpleGrantedAuthority(a.getName()))
.collect(Collectors.toSet());
UsernamePasswordAuthenticationToken token
= new UsernamePasswordAuthenticationToken(
user.getId(),
auth.getCredentials().toString(),
authorities);
return token;
}
/**
* Creates a new UserDTO with basic info.
* @param loginResponse Response from peloton login API
* @param authentication Contains user login info (namely username and password)
* @return UserDTO
*/
private UserDTO createUserDTO(LoginResponse loginResponse, Authentication authentication) {
UserDTO dto = new UserDTO();
dto.setActivated(true);
dto.setEmail(loginResponse.getEmail());
dto.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
dto.setFirstName(loginResponse.getFirstName());
dto.setLastName(loginResponse.getLastName());
return dto;
}
}

View File

@ -0,0 +1,63 @@
package com.baeldung.jhipster6.security;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.repository.UserRepository;
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* Authenticate a user from the database.
*/
@Component("userDetailsService")
public class DomainUserDetailsService implements UserDetailsService {
private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
private final UserRepository userRepository;
public DomainUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
if (new EmailValidator().isValid(login, null)) {
return userRepository.findOneWithAuthoritiesByEmail(login)
.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("User with email " + login + " was not found in the database"));
}
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
return userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin)
.map(user -> createSpringSecurityUser(lowercaseLogin, user))
.orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.getActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(),
user.getPassword(),
grantedAuthorities);
}
}

View File

@ -0,0 +1,76 @@
package com.baeldung.jhipster6.security;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Optional;
/**
* Utility class for Spring Security.
*/
public final class SecurityUtils {
private SecurityUtils() {
}
/**
* Get the login of the current user.
*
* @return the login of the current user
*/
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
});
}
/**
* Get the JWT of the current user.
*
* @return the JWT of the current user
*/
public static Optional<String> getCurrentUserJWT() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.filter(authentication -> authentication.getCredentials() instanceof String)
.map(authentication -> (String) authentication.getCredentials());
}
/**
* Check if a user is authenticated.
*
* @return true if the user is authenticated, false otherwise
*/
public static boolean isAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> authentication.getAuthorities().stream()
.noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS)))
.orElse(false);
}
/**
* If the current user has a specific authority (security role).
* <p>
* The name of this method comes from the isUserInRole() method in the Servlet API
*
* @param authority the authority to check
* @return true if the current user has the authority, false otherwise
*/
public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> authentication.getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority)))
.orElse(false);
}
}

View File

@ -0,0 +1,20 @@
package com.baeldung.jhipster6.security;
import com.baeldung.jhipster6.config.Constants;
import java.util.Optional;
import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
/**
* Implementation of AuditorAware based on Spring Security.
*/
@Component
public class SpringSecurityAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of(SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM_ACCOUNT));
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.jhipster6.security;
import org.springframework.security.core.AuthenticationException;
/**
* This exception is thrown in case of a not activated user trying to authenticate.
*/
public class UserNotActivatedException extends AuthenticationException {
private static final long serialVersionUID = 1L;
public UserNotActivatedException(String message) {
super(message);
}
public UserNotActivatedException(String message, Throwable t) {
super(message, t);
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.jhipster6.security.dto;
/**
* Simple DTO representing a login request to a remote service.
*/
public class LoginRequest {
private String username;
private String password;
public LoginRequest() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.jhipster6.security.dto;
/**
* Simple DTO representing the response of logging in using a remote service.
*/
public class LoginResponse {
private String username;
private String firstName;
private String lastName;
private String email;
public LoginResponse() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -0,0 +1,21 @@
package com.baeldung.jhipster6.security.jwt;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private TokenProvider tokenProvider;
public JWTConfigurer(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void configure(HttpSecurity http) throws Exception {
JWTFilter customFilter = new JWTFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.jhipster6.security.jwt;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is
* found.
*/
public class JWTFilter extends GenericFilterBean {
public static final String AUTHORIZATION_HEADER = "Authorization";
private TokenProvider tokenProvider;
public JWTFilter(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String resolveToken(HttpServletRequest request){
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}

View File

@ -0,0 +1,119 @@
package com.baeldung.jhipster6.security.jwt;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import io.github.jhipster.config.JHipsterProperties;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
@Component
public class TokenProvider {
private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
private static final String AUTHORITIES_KEY = "auth";
private Key key;
private long tokenValidityInMilliseconds;
private long tokenValidityInMillisecondsForRememberMe;
private final JHipsterProperties jHipsterProperties;
public TokenProvider(JHipsterProperties jHipsterProperties) {
this.jHipsterProperties = jHipsterProperties;
}
@PostConstruct
public void init() {
byte[] keyBytes;
String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret();
if (!StringUtils.isEmpty(secret)) {
log.warn("Warning: the JWT key used is not Base64-encoded. " +
"We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security.");
keyBytes = secret.getBytes(StandardCharsets.UTF_8);
} else {
log.debug("Using a Base64-encoded JWT secret key");
keyBytes = Decoders.BASE64.decode(jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret());
}
this.key = Keys.hmacShaKeyFor(keyBytes);
this.tokenValidityInMilliseconds =
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds();
this.tokenValidityInMillisecondsForRememberMe =
1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt()
.getTokenValidityInSecondsForRememberMe();
}
public String createToken(Authentication authentication, boolean rememberMe) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
long now = (new Date()).getTime();
Date validity;
if (rememberMe) {
validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
} else {
validity = new Date(now + this.tokenValidityInMilliseconds);
}
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(key, SignatureAlgorithm.HS512)
.setExpiration(validity)
.compact();
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
log.info("Invalid JWT signature.");
log.trace("Invalid JWT signature trace: {}", e);
} catch (ExpiredJwtException e) {
log.info("Expired JWT token.");
log.trace("Expired JWT token trace: {}", e);
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token.");
log.trace("Unsupported JWT token trace: {}", e);
} catch (IllegalArgumentException e) {
log.info("JWT token compact of handler are invalid.");
log.trace("JWT token compact of handler are invalid trace: {}", e);
}
return false;
}
}

View File

@ -0,0 +1,4 @@
/**
* Spring Security configuration.
*/
package com.baeldung.jhipster6.security;

View File

@ -0,0 +1,52 @@
package com.baeldung.jhipster6.service;
import com.baeldung.jhipster6.config.audit.AuditEventConverter;
import com.baeldung.jhipster6.repository.PersistenceAuditEventRepository;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.Optional;
/**
* Service for managing audit events.
* <p>
* This is the default implementation to support SpringBoot Actuator AuditEventRepository
*/
@Service
@Transactional
public class AuditEventService {
private final PersistenceAuditEventRepository persistenceAuditEventRepository;
private final AuditEventConverter auditEventConverter;
public AuditEventService(
PersistenceAuditEventRepository persistenceAuditEventRepository,
AuditEventConverter auditEventConverter) {
this.persistenceAuditEventRepository = persistenceAuditEventRepository;
this.auditEventConverter = auditEventConverter;
}
public Page<AuditEvent> findAll(Pageable pageable) {
return persistenceAuditEventRepository.findAll(pageable)
.map(auditEventConverter::convertToAuditEvent);
}
public Page<AuditEvent> findByDates(Instant fromDate, Instant toDate, Pageable pageable) {
return persistenceAuditEventRepository.findAllByAuditEventDateBetween(fromDate, toDate, pageable)
.map(auditEventConverter::convertToAuditEvent);
}
public Optional<AuditEvent> find(Long id) {
return Optional.ofNullable(persistenceAuditEventRepository.findById(id))
.filter(Optional::isPresent)
.map(Optional::get)
.map(auditEventConverter::convertToAuditEvent);
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.jhipster6.service;
import com.baeldung.jhipster6.service.dto.BookDTO;
import java.util.List;
import java.util.Optional;
/**
* Service Interface for managing Book.
*/
public interface BookService {
/**
* Save a book.
*
* @param bookDTO the entity to save
* @return the persisted entity
*/
BookDTO save(BookDTO bookDTO);
/**
* Get all the books.
*
* @return the list of entities
*/
List<BookDTO> findAll();
/**
* Get the "id" book.
*
* @param id the id of the entity
* @return the entity
*/
Optional<BookDTO> findOne(Long id);
/**
* Delete the "id" book.
*
* @param id the id of the entity
*/
void delete(Long id);
/**
* Simulates purchasing a book by reducing the stock of a book by 1.
* @param id the id of the book
* @return Updated BookDTO, empty if not found, or throws exception if an error occurs.
*/
Optional<BookDTO> purchase(Long id);
}

View File

@ -0,0 +1,105 @@
package com.baeldung.jhipster6.service;
import com.baeldung.jhipster6.domain.User;
import io.github.jhipster.config.JHipsterProperties;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
/**
* Service for sending emails.
* <p>
* We use the @Async annotation to send emails asynchronously.
*/
@Service
public class MailService {
private final Logger log = LoggerFactory.getLogger(MailService.class);
private static final String USER = "user";
private static final String BASE_URL = "baseUrl";
private final JHipsterProperties jHipsterProperties;
private final JavaMailSender javaMailSender;
private final MessageSource messageSource;
private final SpringTemplateEngine templateEngine;
public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender,
MessageSource messageSource, SpringTemplateEngine templateEngine) {
this.jHipsterProperties = jHipsterProperties;
this.javaMailSender = javaMailSender;
this.messageSource = messageSource;
this.templateEngine = templateEngine;
}
@Async
public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
log.debug("Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
isMultipart, isHtml, to, subject, content);
// Prepare message using a Spring helper
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, StandardCharsets.UTF_8.name());
message.setTo(to);
message.setFrom(jHipsterProperties.getMail().getFrom());
message.setSubject(subject);
message.setText(content, isHtml);
javaMailSender.send(mimeMessage);
log.debug("Sent email to User '{}'", to);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.warn("Email could not be sent to user '{}'", to, e);
} else {
log.warn("Email could not be sent to user '{}': {}", to, e.getMessage());
}
}
}
@Async
public void sendEmailFromTemplate(User user, String templateName, String titleKey) {
Locale locale = Locale.forLanguageTag(user.getLangKey());
Context context = new Context(locale);
context.setVariable(USER, user);
context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
String content = templateEngine.process(templateName, context);
String subject = messageSource.getMessage(titleKey, null, locale);
sendEmail(user.getEmail(), subject, content, false, true);
}
@Async
public void sendActivationEmail(User user) {
log.debug("Sending activation email to '{}'", user.getEmail());
sendEmailFromTemplate(user, "mail/activationEmail", "email.activation.title");
}
@Async
public void sendCreationEmail(User user) {
log.debug("Sending creation email to '{}'", user.getEmail());
sendEmailFromTemplate(user, "mail/creationEmail", "email.activation.title");
}
@Async
public void sendPasswordResetMail(User user) {
log.debug("Sending password reset email to '{}'", user.getEmail());
sendEmailFromTemplate(user, "mail/passwordResetEmail", "email.reset.title");
}
}

View File

@ -0,0 +1,276 @@
package com.baeldung.jhipster6.service;
import com.baeldung.jhipster6.config.Constants;
import com.baeldung.jhipster6.domain.Authority;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.repository.AuthorityRepository;
import com.baeldung.jhipster6.repository.UserRepository;
import com.baeldung.jhipster6.security.AuthoritiesConstants;
import com.baeldung.jhipster6.security.SecurityUtils;
import com.baeldung.jhipster6.service.dto.UserDTO;
import com.baeldung.jhipster6.service.util.RandomUtil;
import com.baeldung.jhipster6.web.rest.errors.*;
import com.baeldung.jhipster6.web.rest.errors.EmailAlreadyUsedException;
import com.baeldung.jhipster6.web.rest.errors.InvalidPasswordException;
import com.baeldung.jhipster6.web.rest.errors.LoginAlreadyUsedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* Service class for managing users.
*/
@Service
@Transactional
public class UserService {
private final Logger log = LoggerFactory.getLogger(UserService.class);
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthorityRepository authorityRepository;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.authorityRepository = authorityRepository;
}
public Optional<User> activateRegistration(String key) {
log.debug("Activating user for activation key {}", key);
return userRepository.findOneByActivationKey(key)
.map(user -> {
// activate given user for the registration key.
user.setActivated(true);
user.setActivationKey(null);
log.debug("Activated user: {}", user);
return user;
});
}
public Optional<User> completePasswordReset(String newPassword, String key) {
log.debug("Reset user password for reset key {}", key);
return userRepository.findOneByResetKey(key)
.filter(user -> user.getResetDate().isAfter(Instant.now().minusSeconds(86400)))
.map(user -> {
user.setPassword(passwordEncoder.encode(newPassword));
user.setResetKey(null);
user.setResetDate(null);
return user;
});
}
public Optional<User> requestPasswordReset(String mail) {
return userRepository.findOneByEmailIgnoreCase(mail)
.filter(User::getActivated)
.map(user -> {
user.setResetKey(RandomUtil.generateResetKey());
user.setResetDate(Instant.now());
return user;
});
}
public User registerUser(UserDTO userDTO, String password) {
userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).ifPresent(existingUser -> {
boolean removed = removeNonActivatedUser(existingUser);
if (!removed) {
throw new LoginAlreadyUsedException();
}
});
userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).ifPresent(existingUser -> {
boolean removed = removeNonActivatedUser(existingUser);
if (!removed) {
throw new EmailAlreadyUsedException();
}
});
User newUser = new User();
String encryptedPassword = passwordEncoder.encode(password);
newUser.setLogin(userDTO.getLogin().toLowerCase());
// new user gets initially a generated password
newUser.setPassword(encryptedPassword);
newUser.setFirstName(userDTO.getFirstName());
newUser.setLastName(userDTO.getLastName());
newUser.setEmail(userDTO.getEmail().toLowerCase());
newUser.setImageUrl(userDTO.getImageUrl());
newUser.setLangKey(userDTO.getLangKey());
// new user is not active
newUser.setActivated(false);
// new user gets registration key
newUser.setActivationKey(RandomUtil.generateActivationKey());
Set<Authority> authorities = new HashSet<>();
authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add);
newUser.setAuthorities(authorities);
userRepository.save(newUser);
log.debug("Created Information for User: {}", newUser);
return newUser;
}
private boolean removeNonActivatedUser(User existingUser){
if (existingUser.getActivated()) {
return false;
}
userRepository.delete(existingUser);
userRepository.flush();
return true;
}
public User createUser(UserDTO userDTO) {
User user = new User();
user.setLogin(userDTO.getLogin().toLowerCase());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail().toLowerCase());
user.setImageUrl(userDTO.getImageUrl());
if (userDTO.getLangKey() == null) {
user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language
} else {
user.setLangKey(userDTO.getLangKey());
}
String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
user.setPassword(encryptedPassword);
user.setResetKey(RandomUtil.generateResetKey());
user.setResetDate(Instant.now());
user.setActivated(true);
if (userDTO.getAuthorities() != null) {
Set<Authority> authorities = userDTO.getAuthorities().stream()
.map(authorityRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
user.setAuthorities(authorities);
}
userRepository.save(user);
log.debug("Created Information for User: {}", user);
return user;
}
/**
* Update basic information (first name, last name, email, language) for the current user.
*
* @param firstName first name of user
* @param lastName last name of user
* @param email email id of user
* @param langKey language key
* @param imageUrl image URL of user
*/
public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) {
SecurityUtils.getCurrentUserLogin()
.flatMap(userRepository::findOneByLogin)
.ifPresent(user -> {
user.setFirstName(firstName);
user.setLastName(lastName);
user.setEmail(email.toLowerCase());
user.setLangKey(langKey);
user.setImageUrl(imageUrl);
log.debug("Changed Information for User: {}", user);
});
}
/**
* Update all information for a specific user, and return the modified user.
*
* @param userDTO user to update
* @return updated user
*/
public Optional<UserDTO> updateUser(UserDTO userDTO) {
return Optional.of(userRepository
.findById(userDTO.getId()))
.filter(Optional::isPresent)
.map(Optional::get)
.map(user -> {
user.setLogin(userDTO.getLogin().toLowerCase());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail().toLowerCase());
user.setImageUrl(userDTO.getImageUrl());
user.setActivated(userDTO.isActivated());
user.setLangKey(userDTO.getLangKey());
Set<Authority> managedAuthorities = user.getAuthorities();
managedAuthorities.clear();
userDTO.getAuthorities().stream()
.map(authorityRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(managedAuthorities::add);
log.debug("Changed Information for User: {}", user);
return user;
})
.map(UserDTO::new);
}
public void deleteUser(String login) {
userRepository.findOneByLogin(login).ifPresent(user -> {
userRepository.delete(user);
log.debug("Deleted User: {}", user);
});
}
public void changePassword(String currentClearTextPassword, String newPassword) {
SecurityUtils.getCurrentUserLogin()
.flatMap(userRepository::findOneByLogin)
.ifPresent(user -> {
String currentEncryptedPassword = user.getPassword();
if (!passwordEncoder.matches(currentClearTextPassword, currentEncryptedPassword)) {
throw new InvalidPasswordException();
}
String encryptedPassword = passwordEncoder.encode(newPassword);
user.setPassword(encryptedPassword);
log.debug("Changed password for User: {}", user);
});
}
@Transactional(readOnly = true)
public Page<UserDTO> getAllManagedUsers(Pageable pageable) {
return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new);
}
@Transactional(readOnly = true)
public Optional<User> getUserWithAuthoritiesByLogin(String login) {
return userRepository.findOneWithAuthoritiesByLogin(login);
}
@Transactional(readOnly = true)
public Optional<User> getUserWithAuthorities(Long id) {
return userRepository.findOneWithAuthoritiesById(id);
}
@Transactional(readOnly = true)
public Optional<User> getUserWithAuthorities() {
return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin);
}
/**
* Not activated users should be automatically deleted after 3 days.
* <p>
* This is scheduled to get fired everyday, at 01:00 (am).
*/
@Scheduled(cron = "0 0 1 * * ?")
public void removeNotActivatedUsers() {
userRepository
.findAllByActivatedIsFalseAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS))
.forEach(user -> {
log.debug("Deleting not activated user {}", user.getLogin());
userRepository.delete(user);
});
}
/**
* @return a list of all the authorities
*/
public List<String> getAuthorities() {
return authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,112 @@
package com.baeldung.jhipster6.service.dto;
import java.time.LocalDate;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Objects;
/**
* A DTO for the Book entity.
*/
public class BookDTO implements Serializable {
private Long id;
@NotNull
private String title;
@NotNull
private String author;
@NotNull
private LocalDate published;
@NotNull
@Min(value = 0)
private Integer quantity;
@NotNull
@DecimalMin(value = "0")
private Double price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public LocalDate getPublished() {
return published;
}
public void setPublished(LocalDate published) {
this.published = published;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BookDTO bookDTO = (BookDTO) o;
if (bookDTO.getId() == null || getId() == null) {
return false;
}
return Objects.equals(getId(), bookDTO.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
@Override
public String toString() {
return "BookDTO{" +
"id=" + getId() +
", title='" + getTitle() + "'" +
", author='" + getAuthor() + "'" +
", published='" + getPublished() + "'" +
", quantity=" + getQuantity() +
", price=" + getPrice() +
"}";
}
}

View File

@ -0,0 +1,35 @@
package com.baeldung.jhipster6.service.dto;
/**
* A DTO representing a password change required data - current and new password.
*/
public class PasswordChangeDTO {
private String currentPassword;
private String newPassword;
public PasswordChangeDTO() {
// Empty constructor needed for Jackson.
}
public PasswordChangeDTO(String currentPassword, String newPassword) {
this.currentPassword = currentPassword;
this.newPassword = newPassword;
}
public String getCurrentPassword() {
return currentPassword;
}
public void setCurrentPassword(String currentPassword) {
this.currentPassword = currentPassword;
}
public String getNewPassword() {
return newPassword;
}
public void setNewPassword(String newPassword) {
this.newPassword = newPassword;
}
}

View File

@ -0,0 +1,199 @@
package com.baeldung.jhipster6.service.dto;
import com.baeldung.jhipster6.config.Constants;
import com.baeldung.jhipster6.domain.Authority;
import com.baeldung.jhipster6.domain.User;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.*;
import java.time.Instant;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A DTO representing a user, with his authorities.
*/
public class UserDTO {
private Long id;
@NotBlank
@Pattern(regexp = Constants.LOGIN_REGEX)
@Size(min = 1, max = 50)
private String login;
@Size(max = 50)
private String firstName;
@Size(max = 50)
private String lastName;
@Email
@Size(min = 5, max = 254)
private String email;
@Size(max = 256)
private String imageUrl;
private boolean activated = false;
@Size(min = 2, max = 6)
private String langKey;
private String createdBy;
private Instant createdDate;
private String lastModifiedBy;
private Instant lastModifiedDate;
private Set<String> authorities;
public UserDTO() {
// Empty constructor needed for Jackson.
}
public UserDTO(User user) {
this.id = user.getId();
this.login = user.getLogin();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.email = user.getEmail();
this.activated = user.getActivated();
this.imageUrl = user.getImageUrl();
this.langKey = user.getLangKey();
this.createdBy = user.getCreatedBy();
this.createdDate = user.getCreatedDate();
this.lastModifiedBy = user.getLastModifiedBy();
this.lastModifiedDate = user.getLastModifiedDate();
this.authorities = user.getAuthorities().stream()
.map(Authority::getName)
.collect(Collectors.toSet());
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public boolean isActivated() {
return activated;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
public String getLangKey() {
return langKey;
}
public void setLangKey(String langKey) {
this.langKey = langKey;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Instant getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Instant createdDate) {
this.createdDate = createdDate;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public Instant getLastModifiedDate() {
return lastModifiedDate;
}
public void setLastModifiedDate(Instant lastModifiedDate) {
this.lastModifiedDate = lastModifiedDate;
}
public Set<String> getAuthorities() {
return authorities;
}
public void setAuthorities(Set<String> authorities) {
this.authorities = authorities;
}
@Override
public String toString() {
return "UserDTO{" +
"login='" + login + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", imageUrl='" + imageUrl + '\'' +
", activated=" + activated +
", langKey='" + langKey + '\'' +
", createdBy=" + createdBy +
", createdDate=" + createdDate +
", lastModifiedBy='" + lastModifiedBy + '\'' +
", lastModifiedDate=" + lastModifiedDate +
", authorities=" + authorities +
"}";
}
}

View File

@ -0,0 +1,4 @@
/**
* Data Transfer Objects.
*/
package com.baeldung.jhipster6.service.dto;

View File

@ -0,0 +1,110 @@
package com.baeldung.jhipster6.service.impl;
import com.baeldung.jhipster6.service.BookService;
import com.baeldung.jhipster6.domain.Book;
import com.baeldung.jhipster6.repository.BookRepository;
import com.baeldung.jhipster6.service.dto.BookDTO;
import com.baeldung.jhipster6.service.mapper.BookMapper;
import com.baeldung.jhipster6.web.rest.errors.BadRequestAlertException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Service Implementation for managing Book.
*/
@Service
@Transactional
public class BookServiceImpl implements BookService {
private final Logger log = LoggerFactory.getLogger(BookServiceImpl.class);
private final BookRepository bookRepository;
private final BookMapper bookMapper;
public BookServiceImpl(BookRepository bookRepository, BookMapper bookMapper) {
this.bookRepository = bookRepository;
this.bookMapper = bookMapper;
}
/**
* Save a book.
*
* @param bookDTO the entity to save
* @return the persisted entity
*/
@Override
public BookDTO save(BookDTO bookDTO) {
log.debug("Request to save Book : {}", bookDTO);
Book book = bookMapper.toEntity(bookDTO);
book = bookRepository.save(book);
return bookMapper.toDto(book);
}
/**
* Get all the books.
*
* @return the list of entities
*/
@Override
@Transactional(readOnly = true)
public List<BookDTO> findAll() {
log.debug("Request to get all Books");
return bookRepository.findAll().stream()
.map(bookMapper::toDto)
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* Get one book by id.
*
* @param id the id of the entity
* @return the entity
*/
@Override
@Transactional(readOnly = true)
public Optional<BookDTO> findOne(Long id) {
log.debug("Request to get Book : {}", id);
return bookRepository.findById(id)
.map(bookMapper::toDto);
}
/**
* Delete the book by id.
*
* @param id the id of the entity
*/
@Override
public void delete(Long id) {
log.debug("Request to delete Book : {}", id);
bookRepository.deleteById(id);
}
@Override
public Optional<BookDTO> purchase(Long id) {
Optional<BookDTO> bookDTO = findOne(id);
if(bookDTO.isPresent()) {
int quantity = bookDTO.get().getQuantity();
if(quantity > 0) {
bookDTO.get().setQuantity(quantity - 1);
Book book = bookMapper.toEntity(bookDTO.get());
book = bookRepository.save(book);
return bookDTO;
}
else {
throw new BadRequestAlertException("Book is not in stock", "book", "notinstock");
}
}
return Optional.empty();
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.jhipster6.service.mapper;
import com.baeldung.jhipster6.domain.*;
import com.baeldung.jhipster6.service.dto.BookDTO;
import com.baeldung.jhipster6.domain.Book;
import org.mapstruct.*;
/**
* Mapper for the entity Book and its DTO BookDTO.
*/
@Mapper(componentModel = "spring", uses = {})
public interface BookMapper extends EntityMapper<BookDTO, Book> {
default Book fromId(Long id) {
if (id == null) {
return null;
}
Book book = new Book();
book.setId(id);
return book;
}
}

View File

@ -0,0 +1,21 @@
package com.baeldung.jhipster6.service.mapper;
import java.util.List;
/**
* Contract for a generic dto to entity mapper.
*
* @param <D> - DTO type parameter.
* @param <E> - Entity type parameter.
*/
public interface EntityMapper <D, E> {
E toEntity(D dto);
D toDto(E entity);
List <E> toEntity(List<D> dtoList);
List <D> toDto(List<E> entityList);
}

View File

@ -0,0 +1,81 @@
package com.baeldung.jhipster6.service.mapper;
import com.baeldung.jhipster6.domain.Authority;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.service.dto.UserDTO;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* Mapper for the entity User and its DTO called UserDTO.
*
* Normal mappers are generated using MapStruct, this one is hand-coded as MapStruct
* support is still in beta, and requires a manual step with an IDE.
*/
@Service
public class UserMapper {
public List<UserDTO> usersToUserDTOs(List<User> users) {
return users.stream()
.filter(Objects::nonNull)
.map(this::userToUserDTO)
.collect(Collectors.toList());
}
public UserDTO userToUserDTO(User user) {
return new UserDTO(user);
}
public List<User> userDTOsToUsers(List<UserDTO> userDTOs) {
return userDTOs.stream()
.filter(Objects::nonNull)
.map(this::userDTOToUser)
.collect(Collectors.toList());
}
public User userDTOToUser(UserDTO userDTO) {
if (userDTO == null) {
return null;
} else {
User user = new User();
user.setId(userDTO.getId());
user.setLogin(userDTO.getLogin());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEmail(userDTO.getEmail());
user.setImageUrl(userDTO.getImageUrl());
user.setActivated(userDTO.isActivated());
user.setLangKey(userDTO.getLangKey());
Set<Authority> authorities = this.authoritiesFromStrings(userDTO.getAuthorities());
user.setAuthorities(authorities);
return user;
}
}
private Set<Authority> authoritiesFromStrings(Set<String> authoritiesAsString) {
Set<Authority> authorities = new HashSet<>();
if(authoritiesAsString != null){
authorities = authoritiesAsString.stream().map(string -> {
Authority auth = new Authority();
auth.setName(string);
return auth;
}).collect(Collectors.toSet());
}
return authorities;
}
public User userFromId(Long id) {
if (id == null) {
return null;
}
User user = new User();
user.setId(id);
return user;
}
}

View File

@ -0,0 +1,4 @@
/**
* MapStruct mappers for mapping domain objects and Data Transfer Objects.
*/
package com.baeldung.jhipster6.service.mapper;

View File

@ -0,0 +1,4 @@
/**
* Service layer beans.
*/
package com.baeldung.jhipster6.service;

View File

@ -0,0 +1,41 @@
package com.baeldung.jhipster6.service.util;
import org.apache.commons.lang3.RandomStringUtils;
/**
* Utility class for generating random Strings.
*/
public final class RandomUtil {
private static final int DEF_COUNT = 20;
private RandomUtil() {
}
/**
* Generate a password.
*
* @return the generated password
*/
public static String generatePassword() {
return RandomStringUtils.randomAlphanumeric(DEF_COUNT);
}
/**
* Generate an activation key.
*
* @return the generated activation key
*/
public static String generateActivationKey() {
return RandomStringUtils.randomNumeric(DEF_COUNT);
}
/**
* Generate a reset key.
*
* @return the generated reset key
*/
public static String generateResetKey() {
return RandomStringUtils.randomNumeric(DEF_COUNT);
}
}

View File

@ -0,0 +1,184 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.repository.UserRepository;
import com.baeldung.jhipster6.security.SecurityUtils;
import com.baeldung.jhipster6.service.MailService;
import com.baeldung.jhipster6.service.UserService;
import com.baeldung.jhipster6.service.dto.PasswordChangeDTO;
import com.baeldung.jhipster6.service.dto.UserDTO;
import com.baeldung.jhipster6.web.rest.errors.*;
import com.baeldung.jhipster6.web.rest.errors.EmailAlreadyUsedException;
import com.baeldung.jhipster6.web.rest.errors.EmailNotFoundException;
import com.baeldung.jhipster6.web.rest.errors.InternalServerErrorException;
import com.baeldung.jhipster6.web.rest.errors.InvalidPasswordException;
import com.baeldung.jhipster6.web.rest.errors.LoginAlreadyUsedException;
import com.baeldung.jhipster6.web.rest.vm.KeyAndPasswordVM;
import com.baeldung.jhipster6.web.rest.vm.ManagedUserVM;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.*;
/**
* REST controller for managing the current user's account.
*/
@RestController
@RequestMapping("/api")
public class AccountResource {
private final Logger log = LoggerFactory.getLogger(AccountResource.class);
private final UserRepository userRepository;
private final UserService userService;
private final MailService mailService;
public AccountResource(UserRepository userRepository, UserService userService, MailService mailService) {
this.userRepository = userRepository;
this.userService = userService;
this.mailService = mailService;
}
/**
* POST /register : register the user.
*
* @param managedUserVM the managed user View Model
* @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect
* @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used
* @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already used
*/
@PostMapping("/register")
@ResponseStatus(HttpStatus.CREATED)
public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
if (!checkPasswordLength(managedUserVM.getPassword())) {
throw new InvalidPasswordException();
}
User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());
mailService.sendActivationEmail(user);
}
/**
* GET /activate : activate the registered user.
*
* @param key the activation key
* @throws RuntimeException 500 (Internal Server Error) if the user couldn't be activated
*/
@GetMapping("/activate")
public void activateAccount(@RequestParam(value = "key") String key) {
Optional<User> user = userService.activateRegistration(key);
if (!user.isPresent()) {
throw new InternalServerErrorException("No user was found for this activation key");
}
}
/**
* GET /authenticate : check if the user is authenticated, and return its login.
*
* @param request the HTTP request
* @return the login if the user is authenticated
*/
@GetMapping("/authenticate")
public String isAuthenticated(HttpServletRequest request) {
log.debug("REST request to check if the current user is authenticated");
return request.getRemoteUser();
}
/**
* GET /account : get the current user.
*
* @return the current user
* @throws RuntimeException 500 (Internal Server Error) if the user couldn't be returned
*/
@GetMapping("/account")
public UserDTO getAccount() {
return userService.getUserWithAuthorities()
.map(UserDTO::new)
.orElseThrow(() -> new InternalServerErrorException("User could not be found"));
}
/**
* POST /account : update the current user information.
*
* @param userDTO the current user information
* @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already used
* @throws RuntimeException 500 (Internal Server Error) if the user login wasn't found
*/
@PostMapping("/account")
public void saveAccount(@Valid @RequestBody UserDTO userDTO) {
String userLogin = SecurityUtils.getCurrentUserLogin().orElseThrow(() -> new InternalServerErrorException("Current user login not found"));
Optional<User> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) {
throw new EmailAlreadyUsedException();
}
Optional<User> user = userRepository.findOneByLogin(userLogin);
if (!user.isPresent()) {
throw new InternalServerErrorException("User could not be found");
}
userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
userDTO.getLangKey(), userDTO.getImageUrl());
}
/**
* POST /account/change-password : changes the current user's password
*
* @param passwordChangeDto current and new password
* @throws InvalidPasswordException 400 (Bad Request) if the new password is incorrect
*/
@PostMapping(path = "/account/change-password")
public void changePassword(@RequestBody PasswordChangeDTO passwordChangeDto) {
if (!checkPasswordLength(passwordChangeDto.getNewPassword())) {
throw new InvalidPasswordException();
}
userService.changePassword(passwordChangeDto.getCurrentPassword(), passwordChangeDto.getNewPassword());
}
/**
* POST /account/reset-password/init : Send an email to reset the password of the user
*
* @param mail the mail of the user
* @throws EmailNotFoundException 400 (Bad Request) if the email address is not registered
*/
@PostMapping(path = "/account/reset-password/init")
public void requestPasswordReset(@RequestBody String mail) {
mailService.sendPasswordResetMail(
userService.requestPasswordReset(mail)
.orElseThrow(EmailNotFoundException::new)
);
}
/**
* POST /account/reset-password/finish : Finish to reset the password of the user
*
* @param keyAndPassword the generated key and the new password
* @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect
* @throws RuntimeException 500 (Internal Server Error) if the password could not be reset
*/
@PostMapping(path = "/account/reset-password/finish")
public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
throw new InvalidPasswordException();
}
Optional<User> user =
userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey());
if (!user.isPresent()) {
throw new InternalServerErrorException("No user was found for this reset key");
}
}
private static boolean checkPasswordLength(String password) {
return !StringUtils.isEmpty(password) &&
password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH;
}
}

View File

@ -0,0 +1,77 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.service.AuditEventService;
import com.baeldung.jhipster6.web.rest.util.PaginationUtil;
import io.github.jhipster.web.util.ResponseUtil;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
/**
* REST controller for getting the audit events.
*/
@RestController
@RequestMapping("/management/audits")
public class AuditResource {
private final AuditEventService auditEventService;
public AuditResource(AuditEventService auditEventService) {
this.auditEventService = auditEventService;
}
/**
* GET /audits : get a page of AuditEvents.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
*/
@GetMapping
public ResponseEntity<List<AuditEvent>> getAll(Pageable pageable) {
Page<AuditEvent> page = auditEventService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits : get a page of AuditEvents between the fromDate and toDate.
*
* @param fromDate the start of the time period of AuditEvents to get
* @param toDate the end of the time period of AuditEvents to get
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of AuditEvents in body
*/
@GetMapping(params = {"fromDate", "toDate"})
public ResponseEntity<List<AuditEvent>> getByDates(
@RequestParam(value = "fromDate") LocalDate fromDate,
@RequestParam(value = "toDate") LocalDate toDate,
Pageable pageable) {
Page<AuditEvent> page = auditEventService.findByDates(
fromDate.atStartOfDay(ZoneId.systemDefault()).toInstant(),
toDate.atStartOfDay(ZoneId.systemDefault()).plusDays(1).toInstant(),
pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/management/audits");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* GET /audits/:id : get an AuditEvent by id.
*
* @param id the id of the entity to get
* @return the ResponseEntity with status 200 (OK) and the AuditEvent in body, or status 404 (Not Found)
*/
@GetMapping("/{id:.+}")
public ResponseEntity<AuditEvent> get(@PathVariable Long id) {
return ResponseUtil.wrapOrNotFound(auditEventService.find(id));
}
}

View File

@ -0,0 +1,118 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.service.BookService;
import com.baeldung.jhipster6.web.rest.errors.BadRequestAlertException;
import com.baeldung.jhipster6.web.rest.util.HeaderUtil;
import com.baeldung.jhipster6.service.dto.BookDTO;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Optional;
/**
* REST controller for managing Book.
*/
@RestController
@RequestMapping("/api")
public class BookResource {
private final Logger log = LoggerFactory.getLogger(BookResource.class);
private static final String ENTITY_NAME = "book";
private final BookService bookService;
public BookResource(BookService bookService) {
this.bookService = bookService;
}
/**
* POST /books : Create a new book.
*
* @param bookDTO the bookDTO to create
* @return the ResponseEntity with status 201 (Created) and with body the new bookDTO, or with status 400 (Bad Request) if the book has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PostMapping("/books")
public ResponseEntity<BookDTO> createBook(@Valid @RequestBody BookDTO bookDTO) throws URISyntaxException {
log.debug("REST request to save Book : {}", bookDTO);
if (bookDTO.getId() != null) {
throw new BadRequestAlertException("A new book cannot already have an ID", ENTITY_NAME, "idexists");
}
BookDTO result = bookService.save(bookDTO);
return ResponseEntity.created(new URI("/api/books/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
/**
* PUT /books : Updates an existing book.
*
* @param bookDTO the bookDTO to update
* @return the ResponseEntity with status 200 (OK) and with body the updated bookDTO,
* or with status 400 (Bad Request) if the bookDTO is not valid,
* or with status 500 (Internal Server Error) if the bookDTO couldn't be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PutMapping("/books")
public ResponseEntity<BookDTO> updateBook(@Valid @RequestBody BookDTO bookDTO) throws URISyntaxException {
log.debug("REST request to update Book : {}", bookDTO);
if (bookDTO.getId() == null) {
throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
}
BookDTO result = bookService.save(bookDTO);
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, bookDTO.getId().toString()))
.body(result);
}
/**
* GET /books : get all the books.
*
* @return the ResponseEntity with status 200 (OK) and the list of books in body
*/
@GetMapping("/books")
public List<BookDTO> getAllBooks() {
log.debug("REST request to get all Books");
return bookService.findAll();
}
/**
* GET /books/:id : get the "id" book.
*
* @param id the id of the bookDTO to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the bookDTO, or with status 404 (Not Found)
*/
@GetMapping("/books/{id}")
public ResponseEntity<BookDTO> getBook(@PathVariable Long id) {
log.debug("REST request to get Book : {}", id);
Optional<BookDTO> bookDTO = bookService.findOne(id);
return ResponseUtil.wrapOrNotFound(bookDTO);
}
/**
* DELETE /books/:id : delete the "id" book.
*
* @param id the id of the bookDTO to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/books/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
log.debug("REST request to delete Book : {}", id);
bookService.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
}
@GetMapping("/books/purchase/{id}")
public ResponseEntity<BookDTO> purchase(@PathVariable Long id) {
Optional<BookDTO> bookDTO = bookService.purchase(id);
return ResponseUtil.wrapOrNotFound(bookDTO);
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.jhipster6.web.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ClientForwardController {
/**
* Forwards any unmapped paths (except those containing a period) to the client {@code index.html}.
* @return forward to client {@code index.html}.
*/
@GetMapping(value = "/**/{path:[^\\.]*}")
public String forward() {
return "forward:/";
}
}

View File

@ -0,0 +1,36 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.web.rest.vm.LoggerVM;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* Controller for view and managing Log Level at runtime.
*/
@RestController
@RequestMapping("/management")
public class LogsResource {
@GetMapping("/logs")
public List<LoggerVM> getList() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
return context.getLoggerList()
.stream()
.map(LoggerVM::new)
.collect(Collectors.toList());
}
@PutMapping("/logs")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void changeLevel(@RequestBody LoggerVM jsonLogger) {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
context.getLogger(jsonLogger.getName()).setLevel(Level.valueOf(jsonLogger.getLevel()));
}
}

View File

@ -0,0 +1,71 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.security.jwt.JWTFilter;
import com.baeldung.jhipster6.security.jwt.TokenProvider;
import com.baeldung.jhipster6.web.rest.vm.LoginVM;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* Controller to authenticate users.
*/
@RestController
@RequestMapping("/api")
public class UserJWTController {
private final TokenProvider tokenProvider;
private final AuthenticationManager authenticationManager;
public UserJWTController(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
@PostMapping("/authenticate")
public ResponseEntity<JWTToken> authorize(@Valid @RequestBody LoginVM loginVM) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
String jwt = tokenProvider.createToken(authentication, rememberMe);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}
/**
* Object to return as body in JWT Authentication.
*/
static class JWTToken {
private String idToken;
JWTToken(String idToken) {
this.idToken = idToken;
}
@JsonProperty("id_token")
String getIdToken() {
return idToken;
}
void setIdToken(String idToken) {
this.idToken = idToken;
}
}
}

View File

@ -0,0 +1,183 @@
package com.baeldung.jhipster6.web.rest;
import com.baeldung.jhipster6.config.Constants;
import com.baeldung.jhipster6.domain.User;
import com.baeldung.jhipster6.repository.UserRepository;
import com.baeldung.jhipster6.security.AuthoritiesConstants;
import com.baeldung.jhipster6.service.MailService;
import com.baeldung.jhipster6.service.UserService;
import com.baeldung.jhipster6.service.dto.UserDTO;
import com.baeldung.jhipster6.web.rest.errors.BadRequestAlertException;
import com.baeldung.jhipster6.web.rest.errors.EmailAlreadyUsedException;
import com.baeldung.jhipster6.web.rest.errors.LoginAlreadyUsedException;
import com.baeldung.jhipster6.web.rest.util.HeaderUtil;
import com.baeldung.jhipster6.web.rest.util.PaginationUtil;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* REST controller for managing users.
* <p>
* This class accesses the User entity, and needs to fetch its collection of authorities.
* <p>
* For a normal use-case, it would be better to have an eager relationship between User and Authority,
* and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
* which would be good for performance.
* <p>
* We use a View Model and a DTO for 3 reasons:
* <ul>
* <li>We want to keep a lazy association between the user and the authorities, because people will
* quite often do relationships with the user, and we don't want them to get the authorities all
* the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
* application because of this use-case.</li>
* <li> Not having an outer join causes n+1 requests to the database. This is not a real issue as
* we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
* but then all authorities come from the cache, so in fact it's much better than doing an outer join
* (which will get lots of data from the database, for each HTTP call).</li>
* <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
* </ul>
* <p>
* Another option would be to have a specific JPA entity graph to handle this case.
*/
@RestController
@RequestMapping("/api")
public class UserResource {
private final Logger log = LoggerFactory.getLogger(UserResource.class);
private final UserService userService;
private final UserRepository userRepository;
private final MailService mailService;
public UserResource(UserService userService, UserRepository userRepository, MailService mailService) {
this.userService = userService;
this.userRepository = userRepository;
this.mailService = mailService;
}
/**
* POST /users : Creates a new user.
* <p>
* Creates a new user if the login and email are not already used, and sends an
* mail with an activation link.
* The user needs to be activated on creation.
*
* @param userDTO the user to create
* @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
* @throws URISyntaxException if the Location URI syntax is incorrect
* @throws BadRequestAlertException 400 (Bad Request) if the login or email is already in use
*/
@PostMapping("/users")
@PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<User> createUser(@Valid @RequestBody UserDTO userDTO) throws URISyntaxException {
log.debug("REST request to save User : {}", userDTO);
if (userDTO.getId() != null) {
throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists");
// Lowercase the user login before comparing with database
} else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) {
throw new LoginAlreadyUsedException();
} else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) {
throw new EmailAlreadyUsedException();
} else {
User newUser = userService.createUser(userDTO);
mailService.sendCreationEmail(newUser);
return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
.headers(HeaderUtil.createAlert( "A user is created with identifier " + newUser.getLogin(), newUser.getLogin()))
.body(newUser);
}
}
/**
* PUT /users : Updates an existing User.
*
* @param userDTO the user to update
* @return the ResponseEntity with status 200 (OK) and with body the updated user
* @throws EmailAlreadyUsedException 400 (Bad Request) if the email is already in use
* @throws LoginAlreadyUsedException 400 (Bad Request) if the login is already in use
*/
@PutMapping("/users")
@PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<UserDTO> updateUser(@Valid @RequestBody UserDTO userDTO) {
log.debug("REST request to update User : {}", userDTO);
Optional<User> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
throw new EmailAlreadyUsedException();
}
existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase());
if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
throw new LoginAlreadyUsedException();
}
Optional<UserDTO> updatedUser = userService.updateUser(userDTO);
return ResponseUtil.wrapOrNotFound(updatedUser,
HeaderUtil.createAlert("A user is updated with identifier " + userDTO.getLogin(), userDTO.getLogin()));
}
/**
* GET /users : get all users.
*
* @param pageable the pagination information
* @return the ResponseEntity with status 200 (OK) and with body all users
*/
@GetMapping("/users")
public ResponseEntity<List<UserDTO>> getAllUsers(Pageable pageable) {
final Page<UserDTO> page = userService.getAllManagedUsers(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
/**
* @return a string list of the all of the roles
*/
@GetMapping("/users/authorities")
@PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")")
public List<String> getAuthorities() {
return userService.getAuthorities();
}
/**
* GET /users/:login : get the "login" user.
*
* @param login the login of the user to find
* @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
*/
@GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
public ResponseEntity<UserDTO> getUser(@PathVariable String login) {
log.debug("REST request to get User : {}", login);
return ResponseUtil.wrapOrNotFound(
userService.getUserWithAuthoritiesByLogin(login)
.map(UserDTO::new));
}
/**
* DELETE /users/:login : delete the "login" User.
*
* @param login the login of the user to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
@PreAuthorize("hasRole(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Void> deleteUser(@PathVariable String login) {
log.debug("REST request to delete User: {}", login);
userService.deleteUser(login);
return ResponseEntity.ok().headers(HeaderUtil.createAlert( "A user is deleted with identifier " + login, login)).build();
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.jhipster6.web.rest.errors;
import org.zalando.problem.AbstractThrowableProblem;
import org.zalando.problem.Status;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
public class BadRequestAlertException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private final String entityName;
private final String errorKey;
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
}
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
this.entityName = entityName;
this.errorKey = errorKey;
}
public String getEntityName() {
return entityName;
}
public String getErrorKey() {
return errorKey;
}
private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("message", "error." + errorKey);
parameters.put("params", entityName);
return parameters;
}
}

View File

@ -0,0 +1,54 @@
package com.baeldung.jhipster6.web.rest.errors;
import org.zalando.problem.AbstractThrowableProblem;
import java.util.HashMap;
import java.util.Map;
import static org.zalando.problem.Status.BAD_REQUEST;
/**
* Custom, parameterized exception, which can be translated on the client side.
* For example:
*
* <pre>
* throw new CustomParameterizedException(&quot;myCustomError&quot;, &quot;hello&quot;, &quot;world&quot;);
* </pre>
*
* Can be translated with:
*
* <pre>
* "error.myCustomError" : "The server says {{param0}} to {{param1}}"
* </pre>
*/
public class CustomParameterizedException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private static final String PARAM = "param";
public CustomParameterizedException(String message, String... params) {
this(message, toParamMap(params));
}
public CustomParameterizedException(String message, Map<String, Object> paramMap) {
super(ErrorConstants.PARAMETERIZED_TYPE, "Parameterized Exception", BAD_REQUEST, null, null, null, toProblemParameters(message, paramMap));
}
public static Map<String, Object> toParamMap(String... params) {
Map<String, Object> paramMap = new HashMap<>();
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
paramMap.put(PARAM + i, params[i]);
}
}
return paramMap;
}
public static Map<String, Object> toProblemParameters(String message, Map<String, Object> paramMap) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("message", message);
parameters.put("params", paramMap);
return parameters;
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.jhipster6.web.rest.errors;
public class EmailAlreadyUsedException extends BadRequestAlertException {
private static final long serialVersionUID = 1L;
public EmailAlreadyUsedException() {
super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email is already in use!", "userManagement", "emailexists");
}
}

Some files were not shown because too many files have changed in this diff Show More