diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f081061..8bc10c4 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -19,14 +19,14 @@ jobs: steps: - name: Checkout source - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '25' distribution: 'zulu' cache: maven @@ -34,21 +34,21 @@ jobs: run: java -version && ./mvnw -version && gpg --version - name: Cache SonarCloud packages - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v6 + uses: crazy-max/ghaction-import-gpg@v7 with: gpg_private_key: ${{ secrets.OSS_SONATYPE_GPG_PRIVATE_KEY }} passphrase: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} @@ -56,8 +56,8 @@ jobs: - name: Build with Maven env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OSS_SONATYPE_USERNAME: michael-schnell + OSS_SONATYPE_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} OSS_SONATYPE_TOKEN: ${{ secrets.OSS_SONATYPE_TOKEN }} OSS_SONATYPE_GPG_PASSPHRASE: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./mvnw clean deploy jacoco:report sonar:sonar -U -B -P sonatype-oss-release --file pom.xml -s settings.xml + run: ./mvnw clean deploy jacoco:report sonar:sonar -U -B -P central-sonatype-release --file pom.xml -s settings.xml diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 0000000..504456f --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1,10 @@ +--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index cb28b0e..0000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 346d645..5291372 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,18 +1,3 @@ -# 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. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip diff --git a/release-notes.md b/CHANGELOG.md similarity index 84% rename from release-notes.md rename to CHANGELOG.md index c67ac46..99f294e 100644 --- a/release-notes.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes +## 0.16.0 +- Added new [Utils4J](src/main/java/org/fuin/utils4j/Utils4J.java) method to encode a UUID to a short string and back +- Added [JSpecify](https://jspecify.dev/) and [NullAway](https://github.com/uber/nullaway) + ## 0.15.0 - Added new [JandexUtils](src/main/java/org/fuin/utils4j/jandex/JandexUtils.java) methods - Added new [TechnicalId](src/main/java/org/fuin/utils4j/TechnicalId.java) tag interface. diff --git a/README.md b/README.md index 906a0ac..0d7209b 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,13 @@ A small Java library that contains several helpful utility classes. [![Java Maven Build](https://github.com/fuinorg/utils4j/actions/workflows/maven.yml/badge.svg)](https://github.com/fuinorg/utils4j/actions/workflows/maven.yml) [![Coverage Status](https://sonarcloud.io/api/project_badges/measure?project=org.fuin%3Autils4j&metric=coverage)](https://sonarcloud.io/dashboard?id=org.fuin%3Autils4j) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.fuin/utils4j/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.fuin/utils4j/) +[![Maven Central](https://img.shields.io/maven-central/v/org.fuin/utils4j.svg)](https://central.sonatype.com/artifact/org.fuin/utils4j) [![Javadocs](https://www.javadoc.io/badge/org.fuin/utils4j.svg)](https://www.javadoc.io/doc/org.fuin/utils4j) [![LGPLv3 License](http://img.shields.io/badge/license-LGPLv3-blue.svg)](https://www.gnu.org/licenses/lgpl.html) [![Java Development Kit 17](https://img.shields.io/badge/JDK-17-green.svg)](https://openjdk.java.net/projects/jdk/17/) ## Versions -- [0.15.0](release-notes.md#0150) -- [0.14.0](release-notes.md#0140) +- See [Release Notes](CHANGELOG.md) - 0.13.x (or later) = **Java 17** - 0.12.0 = **Java 11** with new **jakarta** namespace - 0.11.x = **Java 11** before namespace change from 'javax' to 'jakarta' @@ -32,6 +31,7 @@ A small Java library that contains several helpful utility classes. * [Wait for code to finish](#wait-for-code-to-finish) (Deprecated in favour of [Awaitility](https://github.com/awaitility/awaitility)) * [Find all JARs and classes in the classpath](#find-all-jars-and-classes-in-the-classpath) * [Analyze classes in the classpath with Jandex](#analyze-classes-in-the-classpath-with-jandex) +* [Convert a UUID to 22 character string and back](#convert-uuid-to-22-character-string-and-back) * * * @@ -269,6 +269,20 @@ A test that shows the usage can be found here: [JandexUtilsTest](src/test/java/o ``` +### Convert UUID to 22 character string and back +Normally UUID as string use 36 characters. This functions use a 22 character representation. +The UUID's MSB/LSB are stored as Base64 encoded string and replaces "+" with "-" and "/" with "_". +This may be helpful in situations where the standard 36 characters representation is too long. + +```Java +UUID uuid1 = UUID.fromString("c9a5ab65-0fae-4ff8-8858-e06ddaf595e7"); +String str = Utils4J.uuid2ShortStr(uuid1); +// MSB/LSB Base64 encoded would be: "yaWrZQ+uT/iIWOBt2vWV5w" +System.out.println(str); // Prints "yaWrZQ-uT_iIWOBt2vWV5w" +UUID uuid2 = Utils4J.shortStr2uuid(str); +System.out.println(uuid2); // Prints "c9a5ab65-0fae-4ff8-8858-e06ddaf595e7" +``` + * * * diff --git a/mvnw b/mvnw index 8d937f4..bd8896b 100755 --- a/mvnw +++ b/mvnw @@ -19,290 +19,277 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.2.0 -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir +# Apache Maven Wrapper startup batch script, version 3.3.4 # # Optional ENV vars # ----------------- -# 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 +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false +# OS specific support. +native_path() { printf %s\\n "$1"; } 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 - JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi - fi - ;; +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; esac -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=$(java-config --jre-home) - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && - JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="$(which javac)" - if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=$(which readlink) - if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then - if $darwin ; then - javaHome="$(dirname "\"$javaExecutable\"")" - javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" - else - javaExecutable="$(readlink -f "\"$javaExecutable\"")" - fi - javaHome="$(dirname "\"$javaExecutable\"")" - javaHome=$(expr "$javaHome" : '\(.*\)/bin') - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" else JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi fi else - JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" - fi -fi + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : -if [ ! -x "$JAVACMD" ] ; 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 - -# 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 + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi fi +} - 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/.." || exit 1; pwd) - fi - # end of workaround +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" done - printf '%s' "$(cd "$basedir" || exit 1; pwd)" + printf %x\\n $h } -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - # Remove \r in case we run on Windows within Git Bash - # and check out the repository with auto CRLF management - # enabled. Otherwise, we may read lines that are delimited with - # \r\n and produce $'-Xarg\r' rather than -Xarg due to word - # splitting rules. - tr -s '\r\n' ' ' < "$1" - fi +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 } -log() { - if [ "$MVNW_VERBOSE" = true ]; then - printf '%s\n' "$1" - fi +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" } -BASE_DIR=$(find_maven_basedir "$(dirname "$0")") -if [ -z "$BASE_DIR" ]; then - exit 1; +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" fi -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -log "$MAVEN_PROJECTBASEDIR" +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac -########################################################################################## -# 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. -########################################################################################## -wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" -if [ -r "$wrapperJarPath" ]; then - log "Found $wrapperJarPath" +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT else - log "Couldn't find $wrapperJarPath, downloading it ..." + die "cannot create temp dir" +fi - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - fi - while IFS="=" read -r key value; do - # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) - safeValue=$(echo "$value" | tr -d '\r') - case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; - esac - done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" - log "Downloading from: $wrapperUrl" - - if $cygwin; then - wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") - fi +mkdir -p -- "${MAVEN_HOME%/*}" - if command -v wget > /dev/null; then - log "Found wget ... using wget" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - log "Found curl ... using curl" - [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - fi - else - log "Falling back to using Java to download" - javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=$(cygpath --path --windows "$javaSource") - javaClass=$(cygpath --path --windows "$javaClass") - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - log " - Compiling MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - log " - Running MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" - fi - fi - fi +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" fi -########################################################################################## -# End of extension -########################################################################################## -# If specified, validate the SHA-256 sum of the Maven wrapper jar file -wrapperSha256Sum="" -while IFS="=" read -r key value; do - case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; - esac -done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" -if [ -n "$wrapperSha256Sum" ]; then - wrapperSha256Result=false - if command -v sha256sum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then - wrapperSha256Result=true +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true fi - elif command -v shasum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then - wrapperSha256Result=true + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true fi else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." - echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 exit 1 fi - if [ $wrapperSha256Result = false ]; then - echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 - echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 - echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 exit 1 fi fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -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") +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi fi -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" -export MAVEN_CMD_LINE_ARGS +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" -# shellcheck disable=SC2086 # safe args -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd index f80fbad..5761d94 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,3 +1,4 @@ +<# : batch portion @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @@ -18,188 +19,171 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.2.0 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir +@REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM @REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM ---------------------------------------------------------------------------- -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file -SET WRAPPER_SHA_256_SUM="" -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) ) -IF NOT %WRAPPER_SHA_256_SUM%=="" ( - powershell -Command "&{"^ - "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ - "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ - " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ - " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ - " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ - " exit 1;"^ - "}"^ - "}" - if ERRORLEVEL 1 goto error -) - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\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% - -cmd /C exit /B %ERROR_CODE% +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 51b328f..addfc65 100644 --- a/pom.xml +++ b/pom.xml @@ -7,14 +7,15 @@ org.fuin pom - 1.9.0 + 2.0.2 utils4j bundle - 0.15.0-SNAPSHOT + 0.16.0 + Utils4J A small Java library that contains several helpful utility classes. - http://www.fuin.org/utils4j/ + https://www.github.com/fuinorg/utils4j/ https://github.com/fuinorg/utils4j/ @@ -31,23 +32,40 @@ utils4j fuinorg https://sonarcloud.io + 2.49.0 + 0.13.4 + + + + org.fuin + bom + 1.0.0 + pom + import + + + + + + org.jspecify + jspecify + + io.smallrye jandex - 3.2.7 true jakarta.xml.bind jakarta.xml.bind-api - 4.0.2 true @@ -56,42 +74,36 @@ org.junit.jupiter junit-jupiter - 5.10.5 test org.assertj assertj-core - 3.26.3 test nl.jqno.equalsverifier equalsverifier - 3.19.2 test org.xmlunit xmlunit-core - 2.10.0 test commons-io commons-io - 2.18.0 test org.glassfish.jaxb jaxb-runtime - 4.0.5 test @@ -104,6 +116,32 @@ org.apache.maven.plugins maven-compiler-plugin + + + + com.google.errorprone + error_prone_core + ${error-prone.version} + + + com.uber.nullaway + nullaway + ${nullaway.version} + + + + + + default-compile + + + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:AnnotatedPackages=org.fuin.utils4j + + + + @@ -139,11 +177,6 @@ - - org.apache.maven.plugins - maven-jdeps-plugin - - org.jacoco jacoco-maven-plugin @@ -152,7 +185,7 @@ io.smallrye jandex-maven-plugin - 3.0.6 + 3.5.3 make-index diff --git a/settings.xml b/settings.xml index 311de47..62659ba 100644 --- a/settings.xml +++ b/settings.xml @@ -6,24 +6,18 @@ - sonatype-nexus-snapshots + central-sonatype ${env.OSS_SONATYPE_USERNAME} ${env.OSS_SONATYPE_TOKEN} - - sonatype-nexus-staging - ${env.OSS_SONATYPE_USERNAME} - ${env.OSS_SONATYPE_TOKEN} - - - standard + central-sonatype-release ${env.OSS_SONATYPE_GPG_PASSPHRASE} @@ -32,12 +26,18 @@ org.fuin:utils4j + + + + + standard + - sonatype.oss.snapshots - Sonatype OSS Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots + central.sonatype.snapshots + Central Sonatype Snapshot Repository + https://central.sonatype.com/repository/maven-snapshots/ false @@ -46,27 +46,15 @@ true - - - sonatype.oss.releases - Sonatype OSS Releases Repository - https://oss.sonatype.org/content/repositories/releases/ - - true - - - false - - - sonatype.oss.snapshots - Sonatype OSS Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots + central.sonatype.snapshots + Central Sonatype Snapshot Repository + https://central.sonatype.com/repository/maven-snapshots/ false @@ -76,18 +64,6 @@ - - sonatype.oss.releases - Sonatype OSS Releases Repository - https://oss.sonatype.org/content/repositories/releases/ - - true - - - false - - - diff --git a/src/main/java/org/fuin/utils4j/ChangeTrackingMap.java b/src/main/java/org/fuin/utils4j/ChangeTrackingMap.java index f0713b2..4520ed0 100644 --- a/src/main/java/org/fuin/utils4j/ChangeTrackingMap.java +++ b/src/main/java/org/fuin/utils4j/ChangeTrackingMap.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.util.*; /** @@ -136,7 +138,7 @@ public final Map getAdded() { return Collections.unmodifiableMap(added); } - private void changeIntern(final K key, final V oldValue, final V newValue) { + private void changeIntern(final K key, @Nullable final V oldValue, @Nullable final V newValue) { if (tagged) { final V addedValue = added.get(key); if (addedValue == null) { @@ -168,7 +170,7 @@ private void changeIntern(final K key, final V oldValue, final V newValue) { } } - private void removeIntern(final K key, final V value) { + private void removeIntern(final K key, @Nullable final V value) { if (tagged) { if (added.get(key) == null) { final V changedValue = changed.get(key); @@ -216,6 +218,7 @@ public final Set> entrySet() { } @Override + @Nullable public final V get(final Object key) { return map.get(key); } @@ -231,6 +234,7 @@ public final Set keySet() { } @Override + @Nullable public final V put(final K key, final V newValue) { final V oldValue = map.put(key, newValue); changeIntern(key, oldValue, newValue); @@ -250,6 +254,7 @@ public final void putAll(final Map newMap) { @SuppressWarnings("unchecked") @Override + @Nullable public final V remove(final Object key) { final V oldValue = map.remove(key); removeIntern((K) key, oldValue); diff --git a/src/main/java/org/fuin/utils4j/PropertiesFile.java b/src/main/java/org/fuin/utils4j/PropertiesFile.java index 9ad5a1e..1ffcc32 100644 --- a/src/main/java/org/fuin/utils4j/PropertiesFile.java +++ b/src/main/java/org/fuin/utils4j/PropertiesFile.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.io.*; import java.nio.channels.FileLock; import java.util.*; @@ -161,19 +163,19 @@ private void merge(final RandomAccessFileInputStream in) throws MergeException, if (prop.hasChanged()) { if (prop.isNew()) { // New property - if (!prop.getValue().equals(currentProp.getValue())) { + if (!Objects.requireNonNull(prop.getValue()).equals(currentProp.getValue())) { problems.add( new MergeException.Problem("Same new property in file with a different value!", prop, currentProp)); } } else { if (prop.isDeleted()) { // Deleted property - if (!prop.getInitialValue().equals(currentProp.getValue())) { + if (!Objects.requireNonNull(prop.getInitialValue()).equals(currentProp.getValue())) { problems.add(new MergeException.Problem("Modified property in file we want to delete!", prop, currentProp)); } } else { // Changed property - if (!prop.getInitialValue().equals(currentProp.getValue())) { + if (!Objects.requireNonNull(prop.getInitialValue()).equals(currentProp.getValue())) { problems.add(new MergeException.Problem("Same property modified in file but different value!", prop, currentProp)); } @@ -299,6 +301,7 @@ public final void save(final String[] comments, final boolean sortByKey) throws } + @Nullable private Property find(final String key) { for (int i = 0; i < props.size(); i++) { final Property prop = props.get(i); @@ -317,6 +320,7 @@ private Property find(final String key) { * * @return Value or null if the key is unknown. */ + @Nullable public final String get(final String key) { final Property prop = find(key); if (prop == null) { @@ -333,6 +337,7 @@ public final String get(final String key) { * * @return Status text or null if the key is unknown. */ + @Nullable public final String getStatus(final String key) { final Property prop = find(key); if (prop == null) { diff --git a/src/main/java/org/fuin/utils4j/PropertiesFilePreferences.java b/src/main/java/org/fuin/utils4j/PropertiesFilePreferences.java index 6e4e0a8..c21e467 100644 --- a/src/main/java/org/fuin/utils4j/PropertiesFilePreferences.java +++ b/src/main/java/org/fuin/utils4j/PropertiesFilePreferences.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -72,7 +74,7 @@ public PropertiesFilePreferences(final PropertiesFilePreferences parent, final F * @param name * Name of the node. */ - private PropertiesFilePreferences(final PropertiesFilePreferences parent, final File dir, final String name) { + private PropertiesFilePreferences(@Nullable final PropertiesFilePreferences parent, final File dir, final String name) { super(parent, name); this.dir = dir; this.file = new PropertiesFile(new File(dir, FILENAME)); @@ -132,6 +134,7 @@ private void mkdirIfNecessary() throws BackingStoreException { } @Override + @Nullable protected final String getSpi(final String key) { loadIfNecessary(); return file.get(key); diff --git a/src/main/java/org/fuin/utils4j/PropertiesFilePreferencesFactory.java b/src/main/java/org/fuin/utils4j/PropertiesFilePreferencesFactory.java index b5934f5..1a0bba0 100644 --- a/src/main/java/org/fuin/utils4j/PropertiesFilePreferencesFactory.java +++ b/src/main/java/org/fuin/utils4j/PropertiesFilePreferencesFactory.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.io.File; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; @@ -37,12 +39,16 @@ public final class PropertiesFilePreferencesFactory implements PreferencesFactor */ public static final String USER_PREF_DIR = "PropertiesFilePreferences.UserDir"; + @Nullable private final String systemPrefDir; + @Nullable private final String userPrefDir; + @Nullable private PropertiesFilePreferences systemRoot = null; + @Nullable private PropertiesFilePreferences userRoot = null; /** @@ -60,14 +66,15 @@ public PropertiesFilePreferencesFactory() { * @param userPrefDir * Path and name of the "user" preferences directory. */ - public PropertiesFilePreferencesFactory(final String systemPrefDir, final String userPrefDir) { + public PropertiesFilePreferencesFactory(@Nullable final String systemPrefDir, @Nullable final String userPrefDir) { super(); this.systemPrefDir = systemPrefDir; this.userPrefDir = userPrefDir; } @Override - public final synchronized Preferences systemRoot() { + @SuppressWarnings("NullAway") // False positive for "systemRoot" + public synchronized Preferences systemRoot() { if (systemRoot == null) { systemRoot = new PropertiesFilePreferences(getValidDir(SYSTEM_PREF_DIR, systemPrefDir)); // Always sync at shutdown @@ -87,7 +94,8 @@ public void run() { } @Override - public final synchronized Preferences userRoot() { + @SuppressWarnings("NullAway") // False positive for "sysuserRoottemRoot" + public synchronized Preferences userRoot() { if (userRoot == null) { userRoot = new PropertiesFilePreferences(getValidDir(USER_PREF_DIR, userPrefDir)); // Always sync at shutdown @@ -116,7 +124,7 @@ public void run() { * * @return Directory reference. */ - private File getValidDir(final String varName, final String dirName) { + private File getValidDir(final String varName, @Nullable final String dirName) { if (dirName == null) { throw new RuntimeException("The system variable '" + varName + "' is not set!"); } diff --git a/src/main/java/org/fuin/utils4j/Property.java b/src/main/java/org/fuin/utils4j/Property.java index 1ccd318..ad86b4b 100644 --- a/src/main/java/org/fuin/utils4j/Property.java +++ b/src/main/java/org/fuin/utils4j/Property.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.io.Serializable; /** @@ -30,8 +32,10 @@ public final class Property implements Comparable, Serializable { private final String key; + @Nullable private final String initialValue; + @Nullable private String value; /** @@ -44,7 +48,7 @@ public final class Property implements Comparable, Serializable { * @param value * Value. */ - public Property(final String key, final String initialValue, final String value) { + public Property(final String key, @Nullable final String initialValue, @Nullable final String value) { super(); Utils4J.checkNotNull("key", key); this.key = key; @@ -57,6 +61,7 @@ public Property(final String key, final String initialValue, final String value) * * @return Actual value. */ + @Nullable public final String getValue() { return value; } @@ -67,7 +72,7 @@ public final String getValue() { * @param value * Value to set. */ - public final void setValue(final String value) { + public final void setValue(@Nullable final String value) { this.value = value; } @@ -76,6 +81,7 @@ public final void setValue(final String value) { * * @return Value at construction time. */ + @Nullable public final String getInitialValue() { return initialValue; } @@ -132,7 +138,7 @@ public final int hashCode() { } @Override - public final boolean equals(final Object obj) { + public final boolean equals(@Nullable final Object obj) { if (this == obj) { return true; } diff --git a/src/main/java/org/fuin/utils4j/Utils4J.java b/src/main/java/org/fuin/utils4j/Utils4J.java index 11be3fe..e7e139c 100644 --- a/src/main/java/org/fuin/utils4j/Utils4J.java +++ b/src/main/java/org/fuin/utils4j/Utils4J.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; @@ -46,6 +48,7 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; @@ -55,6 +58,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.Date; import java.util.Enumeration; @@ -62,6 +66,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; import java.util.concurrent.Semaphore; import java.util.function.Consumer; import java.util.function.Predicate; @@ -213,8 +218,7 @@ public static Object createInstance(final String className, final ClassLoader cl public static boolean containsURL(final URL[] urls, final URL url) { checkNotNull("urls", urls); checkNotNull("url", url); - for (int i = 0; i < urls.length; i++) { - final URL element = urls[i]; + for (final URL element : urls) { final String elementStr = element.toExternalForm(); final String urlStr = url.toExternalForm(); if (elementStr.equals(urlStr)) { @@ -363,7 +367,7 @@ public static byte[] decryptPasswordBased(final String algorithm, final byte[] e * @param filename Filename without path - Cannot be null. * @return URL. */ - public static URL createUrl(final URL baseUrl, final String path, final String filename) { + public static URL createUrl(final URL baseUrl, @Nullable final String path, final String filename) { checkNotNull("baseUrl", baseUrl); checkNotNull("filename", filename); try { @@ -372,7 +376,7 @@ public static URL createUrl(final URL baseUrl, final String path, final String f baseUrlStr = baseUrlStr + SLASH; } final String pathStr; - if ((path == null) || (path.length() == 0)) { + if ((path == null) || (path.isEmpty())) { pathStr = ""; } else { if (path.endsWith(SLASH)) { @@ -399,8 +403,8 @@ public static String getRelativePath(final File baseDir, final File dir) { checkNotNull("baseDir", baseDir); checkNotNull("dir", dir); - final String base = getCanonicalPath(baseDir); - final String path = getCanonicalPath(dir); + final String base = Objects.requireNonNull(getCanonicalPath(baseDir)); + final String path = Objects.requireNonNull(getCanonicalPath(dir)); if (!path.startsWith(base)) { throw new IllegalArgumentException("The path '" + path + "' is not inside the base directory '" + base + "'!"); } @@ -421,8 +425,8 @@ public static boolean fileInsideDirectory(final File dir, final File file) { checkNotNull("dir", dir); checkNotNull("file", file); - final String dirPath = getCanonicalPath(dir); - final String filePath = getCanonicalPath(file); + final String dirPath = Objects.requireNonNull(getCanonicalPath(dir)); + final String filePath = Objects.requireNonNull(getCanonicalPath(file)); return filePath.startsWith(dirPath); } @@ -433,7 +437,8 @@ public static boolean fileInsideDirectory(final File dir, final File file) { * @param file File to return the canonical path for or null. * @return Canonical path for the given argument or null if the input was null. */ - public static String getCanonicalPath(final File file) { + @Nullable + public static String getCanonicalPath(@Nullable final File file) { if (file == null) { return null; } @@ -451,7 +456,8 @@ public static String getCanonicalPath(final File file) { * @param file File to return the canonical file for or null. * @return Canonical file for the given argument or null if the input was null. */ - public static File getCanonicalFile(final File file) { + @Nullable + public static File getCanonicalFile(@Nullable final File file) { if (file == null) { return null; } @@ -513,7 +519,7 @@ public static void checkNotNull(final String name, final Object value) { * @param value Value to check for an empty String - Cannot be null. */ public static void checkNotEmpty(final String name, final String value) { - if (value.length() == 0) { + if (value.isEmpty()) { throw new IllegalArgumentException("The argument '" + name + "' cannot be empty"); } } @@ -526,7 +532,7 @@ public static void checkNotEmpty(final String name, final String value) { * @param argTypes The list of parameters - Can be null. * @return Textual signature of the method. */ - private static String getMethodSignature(final String returnType, final String methodName, final Class[] argTypes) { + private static String getMethodSignature(@Nullable final String returnType, final String methodName, final Class[] argTypes) { final StringBuilder sb = new StringBuilder(); if (returnType != null) { sb.append(returnType); @@ -556,7 +562,8 @@ private static String getMethodSignature(final String returnType, final String m * @return The result of dispatching the method represented by this object on obj with parameters args. * @throws InvokeMethodFailedException Invoking the method failed for some reason. */ - public static Object invoke(final Object obj, final String methodName, final Class[] argTypes, final Object[] args) + @Nullable + public static Object invoke(final Object obj, final String methodName, @Nullable final Class[] argTypes, @Nullable final Object[] args) throws InvokeMethodFailedException { checkNotNull("obj", obj); @@ -635,7 +642,7 @@ public static void unzip(final File zipFile, final File destDir) throws IOExcept * @param cancelable Signals if the unzip should be canceled - Can be null if no cancel option is required. * @throws IOException Error unzipping the file. */ - public static void unzip(final File zipFile, final File destDir, final UnzipInputStreamWrapper wrapper, final Cancelable cancelable) + public static void unzip(final File zipFile, final File destDir, @Nullable final UnzipInputStreamWrapper wrapper, @Nullable final Cancelable cancelable) throws IOException { checkNotNull("zipFile", zipFile); @@ -693,7 +700,7 @@ public static File getUserHomeDir() { throw new IllegalStateException(errMsg + " not found!"); } final String userHome = str.trim(); - if (userHome.length() == 0) { + if (userHome.isEmpty()) { throw new IllegalStateException(errMsg + " is empty!"); } final File dir = new File(userHome); @@ -716,7 +723,7 @@ public static File getTempDir() { throw new IllegalStateException("System property '" + TEMP_DIR_KEY + "' not found!"); } final String tempDirStr = str.trim(); - if (tempDirStr.length() == 0) { + if (tempDirStr.isEmpty()) { throw new IllegalStateException("System property '" + TEMP_DIR_KEY + "' is empty!"); } final File dir = new File(tempDirStr); @@ -735,9 +742,10 @@ public static File getTempDir() { * @param vars Map with key/values (both of type String - May be null. * @return String with replaced variables. Unknown variables will remain unchanged. */ - public static String replaceVars(final String str, final Map vars) { + @Nullable + public static String replaceVars(@Nullable final String str, @Nullable final Map vars) { - if ((str == null) || (str.length() == 0) || (vars == null) || (vars.size() == 0)) { + if ((str == null) || (str.isEmpty()) || (vars == null) || (vars.isEmpty())) { return str; } @@ -789,7 +797,7 @@ public static long dateToFileTime(final Date date) { /** * Creates an URL Link on the Windows Desktop. This is done by creating a file (URL File Format) with an ".url" extension. For a - * description see http://www.cyanwerks.com/file-format-url.html . + * description see file-format-url . * * @param baseUrl Base URL for the link - Cannot be null or empty. * @param url Target URL - Cannot be null or empty. @@ -841,7 +849,7 @@ public static void createWindowsDesktopUrlLink(final String baseUrl, final Strin /** * Creates the content of an URL Link file (.url) on the Windows Desktop. For a description see - * http://www.cyanwerks.com/file-format-url.html . + * file-format-url. . * * @param baseUrl Base URL for the link - Cannot be null or empty. * @param url Target URL - Cannot be null or empty. @@ -862,8 +870,9 @@ public static void createWindowsDesktopUrlLink(final String baseUrl, final Strin * @return INI file text. */ // CHECKSTYLE:OFF - public static String createWindowsDesktopUrlLinkContent(final String baseUrl, final String url, final File workingDir, - final Integer showCommand, final Integer iconIndex, final File iconFile, final Integer hotKey, final Date modified) { + public static String createWindowsDesktopUrlLinkContent(final String baseUrl, final String url, @Nullable final File workingDir, + final Integer showCommand, final Integer iconIndex, final File iconFile, + final Integer hotKey, final Date modified) { // CHECKSTYLE:ON checkNotNull("baseUrl", baseUrl); @@ -906,7 +915,7 @@ public static String createWindowsDesktopUrlLinkContent(final String baseUrl, fi * @param separator Separator for directories - Can be null or an empty string. * @return Path and filename divided by the separator. */ - public static String concatPathAndFilename(final String path, final String filename, final String separator) { + public static String concatPathAndFilename(@Nullable final String path, final String filename, final String separator) { checkNotNull("filename", filename); checkNotNull("separator", separator); @@ -916,7 +925,7 @@ public static String concatPathAndFilename(final String path, final String filen return filename; } final String trimmedPath = path.trim(); - if (trimmedPath.length() == 0) { + if (trimmedPath.isEmpty()) { return filename; } final String trimmedFilename = filename.trim(); @@ -945,9 +954,9 @@ public static String encodeHex(final byte[] data) { final char[] out = new char[l << 1]; // two characters form the hex value. int j = 0; - for (int i = 0; i < l; i++) { - out[j++] = DIGITS[(0xF0 & data[i]) >>> 4]; - out[j++] = DIGITS[0x0F & data[i]]; + for (byte datum : data) { + out[j++] = DIGITS[(0xF0 & datum) >>> 4]; + out[j++] = DIGITS[0x0F & datum]; } return String.copyValueOf(out); } @@ -1059,7 +1068,7 @@ private static void ignore() { * @param out Destination stream - Cannot be null. * @throws IOException Error writing to the output stream. */ - private static void zipFile(final File srcFile, final String destPath, final ZipOutputStream out) throws IOException { + private static void zipFile(final File srcFile, @Nullable final String destPath, final ZipOutputStream out) throws IOException { final byte[] buf = new byte[1024]; try (final InputStream in = new BufferedInputStream(new FileInputStream(srcFile))) { @@ -1079,9 +1088,9 @@ private static void zipFile(final File srcFile, final String destPath, final Zip * * @param srcDir Directory to list the files for - Cannot be null and must be a valid directory. * @param filter Filter or null for all files. - * @return List of child entries of the directory. + * @return Array of child entries of the directory. */ - private static File[] listFiles(final File srcDir, final FileFilter filter) { + private static File[] listFiles(final File srcDir, @Nullable final FileFilter filter) { final File[] files; if (filter == null) { @@ -1089,6 +1098,9 @@ private static File[] listFiles(final File srcDir, final FileFilter filter) { } else { files = srcDir.listFiles(filter); } + if (files == null) { + return new File[] {}; + } return files; } @@ -1102,15 +1114,15 @@ private static File[] listFiles(final File srcDir, final FileFilter filter) { * @param out Destination stream - Cannot be null. * @throws IOException Error writing to the output stream. */ - private static void zipDir(final File srcDir, final FileFilter filter, final String destPath, final ZipOutputStream out) + private static void zipDir(final File srcDir, @Nullable final FileFilter filter, @Nullable final String destPath, final ZipOutputStream out) throws IOException { final File[] files = listFiles(srcDir, filter); - for (int i = 0; i < files.length; i++) { - if (files[i].isDirectory()) { - zipDir(files[i], filter, concatPathAndFilename(destPath, files[i].getName(), File.separator), out); + for (File file : files) { + if (file.isDirectory()) { + zipDir(file, filter, concatPathAndFilename(destPath, file.getName(), File.separator), out); } else { - zipFile(files[i], destPath, out); + zipFile(file, destPath, out); } } @@ -1126,7 +1138,7 @@ private static void zipDir(final File srcDir, final FileFilter filter, final Str * @param destFile Target ZIP file - Cannot be null. * @throws IOException Error writing to the output stream. */ - public static void zipDir(final File srcDir, final FileFilter filter, final String destPath, final File destFile) throws IOException { + public static void zipDir(final File srcDir, @Nullable final FileFilter filter, @Nullable final String destPath, final File destFile) throws IOException { Utils4J.checkNotNull("srcDir", srcDir); Utils4J.checkValidDir(srcDir); @@ -1146,7 +1158,7 @@ public static void zipDir(final File srcDir, final FileFilter filter, final Stri * @param destFile Target ZIP file - Cannot be null. * @throws IOException Error writing to the output stream. */ - public static void zipDir(final File srcDir, final String destPath, final File destFile) throws IOException { + public static void zipDir(final File srcDir, @Nullable final String destPath, final File destFile) throws IOException { zipDir(srcDir, null, destPath, destFile); @@ -1158,7 +1170,7 @@ public static void zipDir(final File srcDir, final String destPath, final File d * @param obj Object to serialize or null. * @return Serialized object or null. */ - public static byte[] serialize(final Object obj) { + public static byte @Nullable [] serialize(@Nullable final Object obj) { if (obj == null) { return null; } @@ -1179,7 +1191,8 @@ public static byte[] serialize(final Object obj) { * @return Object created from data or null. */ @SuppressWarnings("unchecked") - public static T deserialize(final byte[] data) { + @Nullable + public static T deserialize(final byte @Nullable [] data) { if (data == null) { return null; } @@ -1219,7 +1232,8 @@ public static String readAsString(final URL url, final String encoding, final in * @param url String to convert into an URL or null. * @return URL or null */ - public static URL url(final String url) { + @Nullable + public static URL url(@Nullable final String url) { if (url == null) { return null; } @@ -1239,7 +1253,8 @@ public static URL url(final String url) { * @param str String to replace or null. * @return Replaced string or null. */ - public static String replaceCrLfTab(final String str) { + @Nullable + public static String replaceCrLfTab(@Nullable final String str) { if (str == null) { return null; } @@ -1290,7 +1305,8 @@ public static void sleep(final long millis) { * @param expectedExceptions Expected exceptions - May be null if any cause is expected. * @return TRUE if the actual exception is one of the expected exceptions. */ - public static boolean expectedCause(final Exception actualException, final Collection> expectedExceptions) { + public static boolean expectedCause(final Exception actualException, + @Nullable final Collection> expectedExceptions) { checkNotNull("actualException", actualException); @@ -1317,7 +1333,7 @@ public static boolean expectedCause(final Exception actualException, final Colle * @return TRUE if the actual exception is one of the expected exceptions. */ public static boolean expectedException(final Exception actualException, - final Collection> expectedExceptions) { + @Nullable final Collection> expectedExceptions) { checkNotNull("actualException", actualException); @@ -1537,4 +1553,37 @@ public static void runLocked(final Semaphore lock, } } + /** + * Converts the UUID to a short string. Normally UUID as string use 36 characters. + * This function returns the UUID as text that only occupies 22 characters. + * It stores MSB/LSB as Base64 encoded string and replaces "+" with "-" and "/" with "_". + * + * @param uuid Value to convert into a short UUID string. + * @return String with 22 characters. + */ + public static String uuid2ShortStr(final UUID uuid) { + final ByteBuffer byteBuffer = ByteBuffer.allocate(16); + byteBuffer.putLong(uuid.getMostSignificantBits()); + byteBuffer.putLong(uuid.getLeastSignificantBits()); + final String base64 = Base64.getEncoder().withoutPadding().encodeToString(byteBuffer.array()); + return base64.replace("/", "_").replaceAll("\\+", "-"); + } + + /** + * Parses a short string created by {@link #uuid2ShortStr(UUID)}. + * + * @param shortStr Short string to parse back to UUID. + * @return UUID. + */ + public static UUID shortStr2uuid(final String shortStr) { + final String base64 = shortStr + .replace("_", "/") + .replace("-", "+"); + final byte[] bytes = Base64.getDecoder().decode(base64); + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + final long msb = byteBuffer.getLong(); + final long lsb = byteBuffer.getLong(8); + return new UUID(msb, lsb); + } + } diff --git a/src/main/java/org/fuin/utils4j/VariableResolver.java b/src/main/java/org/fuin/utils4j/VariableResolver.java index ac623dd..d825358 100644 --- a/src/main/java/org/fuin/utils4j/VariableResolver.java +++ b/src/main/java/org/fuin/utils4j/VariableResolver.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j; +import org.jspecify.annotations.Nullable; + import java.util.*; /** @@ -36,7 +38,7 @@ public final class VariableResolver { * @param unresolved * Map to use - May be null. */ - public VariableResolver(final Map unresolved) { + public VariableResolver(@Nullable final Map unresolved) { if (unresolved == null) { this.unresolved = new HashMap<>(); } else { @@ -49,22 +51,18 @@ public VariableResolver(final Map unresolved) { private void resolve() { int max = 0; - Iterator it = unresolved.keySet().iterator(); - while (it.hasNext()) { - final String name = it.next(); + for (String name : unresolved.keySet()) { final String value = unresolved.get(name); - final int d = resolve(name, value, new ArrayList()); + final int d = resolve(name, value, new ArrayList<>()); if (d > max) { max = d; } } for (int d = 0; d <= max; d++) { - it = unresolved.keySet().iterator(); - while (it.hasNext()) { - final String name = it.next(); + for (String name : unresolved.keySet()) { final String value = unresolved.get(name); - if (depth.get(name).intValue() == d) { + if (Objects.requireNonNull(depth.get(name)) == d) { resolved.put(name, Utils4J.replaceCrLfTab(Utils4J.replaceVars(value, resolved))); } } @@ -72,35 +70,32 @@ private void resolve() { } - private Integer resolve(final String name, final String value, final List path) { + private int resolve(final String name, @Nullable final String value, final List path) { // Check for cycles if (path.contains(name)) { final StringBuilder sb = new StringBuilder(); - final Iterator it = path.iterator(); - while (it.hasNext()) { - sb.append(it.next() + " > "); + for (String s : path) { + sb.append(s).append(" > "); } sb.append(name); throw new IllegalStateException("Cycle: " + sb); } // Analyze - Integer d = depth.get(name); - if (d == null) { - final Set refs = references(value); - if (refs.isEmpty()) { - d = 0; - } else { - final Iterator it = refs.iterator(); - while (it.hasNext()) { - final String refName = it.next(); - final String refValue = unresolved.get(refName); - d = 1 + resolve(refName, refValue, add(path, name)); - } + final Integer existing = depth.get(name); + if (existing != null) { + return existing; + } + int d = 0; + final Set refs = references(value); + if (!refs.isEmpty()) { + for (String refName : refs) { + final String refValue = unresolved.get(refName); + d = 1 + resolve(refName, refValue, add(path, name)); } - depth.put(name, d); } + depth.put(name, d); return d; } @@ -145,10 +140,10 @@ public final Map getUnresolved() { * * @return Referenced variable names - Never null, but may be empty. */ - public static Set references(final String value) { + public static Set references(@Nullable final String value) { final HashSet names = new HashSet<>(); - if ((value == null) || (value.length() == 0)) { + if ((value == null) || (value.isEmpty())) { return names; } diff --git a/src/main/java/org/fuin/utils4j/classpath/package-info.java b/src/main/java/org/fuin/utils4j/classpath/package-info.java new file mode 100644 index 0000000..b62c983 --- /dev/null +++ b/src/main/java/org/fuin/utils4j/classpath/package-info.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2015 Michael Schnell. All rights reserved. + * http://www.fuin.org/ + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see http://www.gnu.org/licenses/. + */ + +/** + * Classpath related utilities. + */ +@NullMarked +package org.fuin.utils4j.classpath; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/fuin/utils4j/fileprocessor/FileHandlerResult.java b/src/main/java/org/fuin/utils4j/fileprocessor/FileHandlerResult.java index cdae226..29efa3b 100644 --- a/src/main/java/org/fuin/utils4j/fileprocessor/FileHandlerResult.java +++ b/src/main/java/org/fuin/utils4j/fileprocessor/FileHandlerResult.java @@ -17,7 +17,10 @@ */ package org.fuin.utils4j.fileprocessor; +import org.jspecify.annotations.Nullable; + import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -103,7 +106,8 @@ public final String toString() { * * @return Enumeration instance or NULL (if argument name was NULL). */ - public static FileHandlerResult fromName(final String name) { + @Nullable + public static FileHandlerResult fromName(@Nullable final String name) { if (name == null) { return null; } @@ -137,9 +141,7 @@ public static boolean isValid(final String name) { private static List asList(final FileHandlerResult... handler) { final List list = new ArrayList<>(handler.length); - for (final FileHandlerResult result : handler) { - list.add(result); - } + list.addAll(Arrays.asList(handler)); return Collections.unmodifiableList(list); } diff --git a/src/main/java/org/fuin/utils4j/fileprocessor/FileOrder.java b/src/main/java/org/fuin/utils4j/fileprocessor/FileOrder.java index 44fded6..44f6253 100644 --- a/src/main/java/org/fuin/utils4j/fileprocessor/FileOrder.java +++ b/src/main/java/org/fuin/utils4j/fileprocessor/FileOrder.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.fileprocessor; +import org.jspecify.annotations.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -97,7 +99,8 @@ public final String toString() { * * @return Enumeration instance or NULL (if argument name was NULL). */ - public static FileOrder fromName(final String name) { + @Nullable + public static FileOrder fromName(@Nullable final String name) { if (name == null) { return null; } diff --git a/src/main/java/org/fuin/utils4j/fileprocessor/package-info.java b/src/main/java/org/fuin/utils4j/fileprocessor/package-info.java index b618b07..46555a9 100644 --- a/src/main/java/org/fuin/utils4j/fileprocessor/package-info.java +++ b/src/main/java/org/fuin/utils4j/fileprocessor/package-info.java @@ -19,4 +19,7 @@ /** * Provides a way to process one or more files, directories and sub directories. */ +@NullMarked package org.fuin.utils4j.fileprocessor; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/fuin/utils4j/filter/AndFilter.java b/src/main/java/org/fuin/utils4j/filter/AndFilter.java index 2d70a63..3e5b1b1 100644 --- a/src/main/java/org/fuin/utils4j/filter/AndFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/AndFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter on a list of sub filters 'and's them all together. */ @@ -49,7 +51,7 @@ public AndFilter(final Filter firstFilter, final Filter secondFilter) { } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { if (getFilterList() == null) { return true; } else { @@ -70,12 +72,12 @@ public final boolean complies(final Object obj) { * * @return Name of the filter (default "and") */ + @Nullable public final String getAndStr() { if (andStr.equals(DEFAULT_AND_STR)) { return null; - } else { - return andStr; } + return andStr; } /** diff --git a/src/main/java/org/fuin/utils4j/filter/BooleanFilter.java b/src/main/java/org/fuin/utils4j/filter/BooleanFilter.java index 3350696..dfcd4d8 100644 --- a/src/main/java/org/fuin/utils4j/filter/BooleanFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/BooleanFilter.java @@ -17,11 +17,14 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter on a Boolean property value. */ public class BooleanFilter implements Filter { + @Nullable private final Boolean constValue; /** @@ -30,7 +33,7 @@ public class BooleanFilter implements Filter { * @param constValue * Value the property is compared with. */ - public BooleanFilter(final Boolean constValue) { + public BooleanFilter(@Nullable final Boolean constValue) { super(); this.constValue = constValue; } @@ -40,22 +43,22 @@ public BooleanFilter(final Boolean constValue) { * * @return Value. */ + @Nullable public final Boolean getConstValue() { return constValue; } @Override - public final boolean complies(final Object value) { + public final boolean complies(@Nullable final Object value) { if (value == null) { return (constValue == null); - } else { - return value.equals(constValue); } + return value.equals(constValue); } @Override public final String toString() { - return " = " + constValue.toString(); + return " = " + constValue; } } diff --git a/src/main/java/org/fuin/utils4j/filter/BooleanPropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/BooleanPropertyFilter.java index 9534f91..aff7166 100644 --- a/src/main/java/org/fuin/utils4j/filter/BooleanPropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/BooleanPropertyFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter on a Boolean value. */ @@ -32,7 +34,7 @@ public class BooleanPropertyFilter extends PropertyFilter { * @param constValue * Value the property is compared with. */ - public BooleanPropertyFilter(final String newPropertyName, final Boolean constValue) { + public BooleanPropertyFilter(final String newPropertyName, @Nullable final Boolean constValue) { super(newPropertyName); this.filter = new BooleanFilter(constValue); } @@ -48,12 +50,13 @@ protected final String[] createGetterNames(final String property) { * * @return Value. */ + @Nullable public final Boolean getConstValue() { return filter.getConstValue(); } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { final Object value = getProperty(obj, getPropertyName()); return filter.complies(value); } diff --git a/src/main/java/org/fuin/utils4j/filter/ComparableFilter.java b/src/main/java/org/fuin/utils4j/filter/ComparableFilter.java index c5f78ad..36d24d6 100644 --- a/src/main/java/org/fuin/utils4j/filter/ComparableFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/ComparableFilter.java @@ -18,6 +18,7 @@ package org.fuin.utils4j.filter; import org.fuin.utils4j.Utils4J; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -47,12 +48,11 @@ public ComparableFilter(final Operator newOperator, final Comparable constValue) } @Override - public final boolean complies(final Object value) { + public final boolean complies(@Nullable final Object value) { if (value == null) { return (constValue == null); - } else { - return simpleCompareTo((Comparable) value); } + return simpleCompareTo((Comparable) value); } /** diff --git a/src/main/java/org/fuin/utils4j/filter/ComparablePropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/ComparablePropertyFilter.java index b78e343..91b7b30 100644 --- a/src/main/java/org/fuin/utils4j/filter/ComparablePropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/ComparablePropertyFilter.java @@ -18,6 +18,7 @@ package org.fuin.utils4j.filter; import org.fuin.utils4j.filter.ComparableFilter.Operator; +import org.jspecify.annotations.Nullable; /** * Defines a filter on a Comparable property. @@ -40,7 +41,7 @@ public ComparablePropertyFilter(final String newPropertyName, final ComparableFi } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { final Comparable value = (Comparable) this.getProperty(obj, this.getPropertyName()); return filter.complies(value); } diff --git a/src/main/java/org/fuin/utils4j/filter/Filter.java b/src/main/java/org/fuin/utils4j/filter/Filter.java index 277c118..94fc5ae 100644 --- a/src/main/java/org/fuin/utils4j/filter/Filter.java +++ b/src/main/java/org/fuin/utils4j/filter/Filter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter condition. */ @@ -24,12 +26,12 @@ public interface Filter { /** * Checks if the argument complies with the condition of the filter. - * + * * @param obj * Object to be tested for compliance - * + * * @return If object complies TRUE else FALSE is returned. */ - public boolean complies(Object obj); + boolean complies(@Nullable Object obj); } diff --git a/src/main/java/org/fuin/utils4j/filter/ListFilter.java b/src/main/java/org/fuin/utils4j/filter/ListFilter.java index eba8c15..8c1face 100644 --- a/src/main/java/org/fuin/utils4j/filter/ListFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/ListFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -114,6 +116,7 @@ protected final String toString(final String opStr) { * * @return Bracket */ + @Nullable public final String getCloseBracket() { if (closeBracket.equals(DEFAULT_CLOSE_BRACKET)) { return null; @@ -127,6 +130,7 @@ public final String getCloseBracket() { * * @return Bracket */ + @Nullable public final String getOpenBracket() { if (openBracket.equals(DEFAULT_OPEN_BRACKET)) { return null; diff --git a/src/main/java/org/fuin/utils4j/filter/OrFilter.java b/src/main/java/org/fuin/utils4j/filter/OrFilter.java index e4b813f..a279d55 100644 --- a/src/main/java/org/fuin/utils4j/filter/OrFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/OrFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter on a list of sub filters all OR'ed together. */ @@ -49,7 +51,7 @@ public OrFilter(final Filter firstFilter, final Filter secondFilter) { } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { if (getFilterList() == null) { return true; } else { @@ -75,6 +77,7 @@ public final String toString() { * * @return Name of the filter (default "or") */ + @Nullable public final String getOrStr() { if (orStr.equals(DEFAULT_OR_STR)) { return null; diff --git a/src/main/java/org/fuin/utils4j/filter/PropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/PropertyFilter.java index 4bfd817..db4de38 100644 --- a/src/main/java/org/fuin/utils4j/filter/PropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/PropertyFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -68,7 +70,8 @@ public final String getPropertyName() { * * @return Value returned via the getter of the property. */ - protected final Object getProperty(final Object obj, final String property) { + @Nullable + protected final Object getProperty(@Nullable final Object obj, @Nullable final String property) { if ((obj == null) || (property == null) || (property.trim().length() == 0)) { return null; } diff --git a/src/main/java/org/fuin/utils4j/filter/RegExprFilter.java b/src/main/java/org/fuin/utils4j/filter/RegExprFilter.java index 4bbbf27..b9fad85 100644 --- a/src/main/java/org/fuin/utils4j/filter/RegExprFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/RegExprFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + import java.util.regex.Pattern; /** @@ -64,7 +66,10 @@ public RegExprFilter(final String pattern) { } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { + if (obj == null) { + return false; + } final String str = obj.toString(); if (type == MATCHES) { return p.matcher(str).matches(); @@ -114,6 +119,7 @@ public final void setType(final int type) { * * @return Type name ("matches", "lookingAt" or "find") or NULL (="lookingAt") */ + @Nullable public final String getTypeName() { if (type == LOOKING_AT) { return null; diff --git a/src/main/java/org/fuin/utils4j/filter/RegExprPropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/RegExprPropertyFilter.java index 738a0d9..85df8cf 100644 --- a/src/main/java/org/fuin/utils4j/filter/RegExprPropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/RegExprPropertyFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter with a regular expression on a property. */ @@ -43,7 +45,7 @@ protected final String[] createGetterNames(final String property) { } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { final String value = (String) this.getProperty(obj, this.getPropertyName()); return filter.complies(value); } @@ -72,6 +74,7 @@ public final void setType(final int type) { * * @return Type name ("matches", "lookingAt" or "find") */ + @Nullable public final String getTypeName() { return filter.getTypeName(); } diff --git a/src/main/java/org/fuin/utils4j/filter/StringFilter.java b/src/main/java/org/fuin/utils4j/filter/StringFilter.java index e7d17da..51a4bae 100644 --- a/src/main/java/org/fuin/utils4j/filter/StringFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/StringFilter.java @@ -18,6 +18,7 @@ package org.fuin.utils4j.filter; import org.fuin.utils4j.Utils4J; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -61,7 +62,7 @@ public StringFilter(final Operator newOperator, final String constValue) { } @Override - public final boolean complies(final Object value) { + public final boolean complies(@Nullable final Object value) { if (value == null) { return (constValue == null); } else { diff --git a/src/main/java/org/fuin/utils4j/filter/StringPropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/StringPropertyFilter.java index 69eee37..78c074c 100644 --- a/src/main/java/org/fuin/utils4j/filter/StringPropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/StringPropertyFilter.java @@ -18,6 +18,7 @@ package org.fuin.utils4j.filter; import org.fuin.utils4j.filter.StringFilter.Operator; +import org.jspecify.annotations.Nullable; /** * Defines a filter on a String property. @@ -62,7 +63,7 @@ public StringPropertyFilter(final String newPropertyName, final String newOperat } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { final String value = (String) this.getProperty(obj, this.getPropertyName()); return filter.complies(value); } diff --git a/src/main/java/org/fuin/utils4j/filter/TokenFilter.java b/src/main/java/org/fuin/utils4j/filter/TokenFilter.java index feb467d..07cd51e 100644 --- a/src/main/java/org/fuin/utils4j/filter/TokenFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/TokenFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + import java.util.StringTokenizer; /** @@ -61,7 +63,7 @@ public final String getConstValue() { } @Override - public final boolean complies(final Object value) { + public final boolean complies(@Nullable final Object value) { return complies((String) value, constValue, separators); } @@ -77,7 +79,7 @@ public final boolean complies(final Object value) { * * @return If object property contains the value as one of the tokens TRUE else FALSE. */ - protected final boolean complies(final String value, final String constValue, final String separators) { + protected final boolean complies(@Nullable final String value, final String constValue, final String separators) { if (value == null) { return false; } else { diff --git a/src/main/java/org/fuin/utils4j/filter/TokenPropertyFilter.java b/src/main/java/org/fuin/utils4j/filter/TokenPropertyFilter.java index 21bf697..6009ff7 100644 --- a/src/main/java/org/fuin/utils4j/filter/TokenPropertyFilter.java +++ b/src/main/java/org/fuin/utils4j/filter/TokenPropertyFilter.java @@ -17,6 +17,8 @@ */ package org.fuin.utils4j.filter; +import org.jspecify.annotations.Nullable; + /** * Defines a filter on a String property. */ @@ -45,7 +47,7 @@ protected final String[] createGetterNames(final String property) { } @Override - public final boolean complies(final Object obj) { + public final boolean complies(@Nullable final Object obj) { final String value = (String) this.getProperty(obj, this.getPropertyName()); return filter.complies(value); } diff --git a/src/main/java/org/fuin/utils4j/filter/package-info.java b/src/main/java/org/fuin/utils4j/filter/package-info.java index d6f17f1..7a6686e 100644 --- a/src/main/java/org/fuin/utils4j/filter/package-info.java +++ b/src/main/java/org/fuin/utils4j/filter/package-info.java @@ -19,4 +19,7 @@ /** * A tiny framework for filtering Java objects. */ +@NullMarked package org.fuin.utils4j.filter; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/fuin/utils4j/jandex/package-info.java b/src/main/java/org/fuin/utils4j/jandex/package-info.java new file mode 100644 index 0000000..a49436a --- /dev/null +++ b/src/main/java/org/fuin/utils4j/jandex/package-info.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2015 Michael Schnell. All rights reserved. + * http://www.fuin.org/ + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see http://www.gnu.org/licenses/. + */ + +/** + * Helpers for reading and writing Jandex index files. + */ +@NullMarked +package org.fuin.utils4j.jandex; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/fuin/utils4j/jaxb/CDataXmlAdapter.java b/src/main/java/org/fuin/utils4j/jaxb/CDataXmlAdapter.java index 8e38c0a..4a83c68 100644 --- a/src/main/java/org/fuin/utils4j/jaxb/CDataXmlAdapter.java +++ b/src/main/java/org/fuin/utils4j/jaxb/CDataXmlAdapter.java @@ -18,6 +18,7 @@ package org.fuin.utils4j.jaxb; import jakarta.xml.bind.annotation.adapters.XmlAdapter; +import org.jspecify.annotations.Nullable; /** * Converts a string into a CDATA XML and back. CAUTION: Only works together with {@link CDataXmlStreamWriter} because otherwise the CDATA @@ -26,7 +27,8 @@ public final class CDataXmlAdapter extends XmlAdapter { @Override - public final String marshal(final String value) throws Exception { + @Nullable + public final String marshal(@Nullable final String value) throws Exception { if (value == null) { return null; } @@ -34,7 +36,8 @@ public final String marshal(final String value) throws Exception { } @Override - public final String unmarshal(final String value) throws Exception { + @Nullable + public final String unmarshal(@Nullable final String value) throws Exception { if (value == null) { return null; } diff --git a/src/main/java/org/fuin/utils4j/jaxb/JaxbUtils.java b/src/main/java/org/fuin/utils4j/jaxb/JaxbUtils.java index 9c4653c..6e396f2 100644 --- a/src/main/java/org/fuin/utils4j/jaxb/JaxbUtils.java +++ b/src/main/java/org/fuin/utils4j/jaxb/JaxbUtils.java @@ -23,6 +23,7 @@ import jakarta.xml.bind.Unmarshaller; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import org.fuin.utils4j.Utils4J; +import org.jspecify.annotations.Nullable; import javax.xml.stream.XMLStreamWriter; import java.io.*; @@ -56,7 +57,8 @@ private JaxbUtils() { * @deprecated Use method {@link #marshal(Marshaller, Object)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static String marshal(final T data, final Class... classesToBeBound) { + @Nullable + public static String marshal(@Nullable final T data, final Class... classesToBeBound) { return marshal(data, null, classesToBeBound); } @@ -77,7 +79,8 @@ public static String marshal(final T data, final Class... classesToBeBoun * @deprecated Use method {@link #marshal(Marshaller, Object)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static String marshal(final T data, final XmlAdapter[] adapters, final Class... classesToBeBound) { + @Nullable + public static String marshal(@Nullable final T data, final XmlAdapter @Nullable [] adapters, final Class... classesToBeBound) { if (data == null) { return null; } @@ -104,7 +107,8 @@ public static String marshal(final T data, final XmlAdapter[] adapters * @deprecated Use method {@link #marshal(Marshaller, Object)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static String marshal(final JAXBContext ctx, final T data) { + @Nullable + public static String marshal(final JAXBContext ctx, @Nullable final T data) { return marshal(ctx, data, null); } @@ -125,7 +129,8 @@ public static String marshal(final JAXBContext ctx, final T data) { * @deprecated Use method {@link #marshal(Marshaller, Object)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static String marshal(final JAXBContext ctx, final T data, final XmlAdapter[] adapters) { + @Nullable + public static String marshal(final JAXBContext ctx, @Nullable final T data, final XmlAdapter @Nullable [] adapters) { if (data == null) { return null; } @@ -151,7 +156,7 @@ public static String marshal(final JAXBContext ctx, final T data, final XmlA * @deprecated Use method {@link #marshal(Marshaller, Object, Writer)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static void marshal(final JAXBContext ctx, final T data, final XmlAdapter[] adapters, final Writer writer) { + public static void marshal(final JAXBContext ctx, @Nullable final T data, final XmlAdapter @Nullable [] adapters, final Writer writer) { if (data == null) { return; } @@ -185,7 +190,7 @@ public static void marshal(final JAXBContext ctx, final T data, final XmlAda * @deprecated Use method {@link #marshal(Marshaller, Object, XMLStreamWriter)} together with {@link MarshallerBuilder} instead */ @Deprecated - public static void marshal(final JAXBContext ctx, final T data, final XmlAdapter[] adapters, final XMLStreamWriter writer) { + public static void marshal(final JAXBContext ctx, @Nullable final T data, final XmlAdapter @Nullable [] adapters, final XMLStreamWriter writer) { if (data == null) { return; } @@ -218,7 +223,8 @@ public static void marshal(final JAXBContext ctx, final T data, final XmlAda * @deprecated Use method {@link #unmarshal(Unmarshaller, String)} together with {@link UnmarshallerBuilder} instead */ @Deprecated - public static T unmarshal(final String xmlData, final Class... classesToBeBound) { + @Nullable + public static T unmarshal(@Nullable final String xmlData, final Class... classesToBeBound) { final UnmarshallerBuilder builder = new UnmarshallerBuilder(); if (classesToBeBound != null) { builder.addClassesToBeBound(classesToBeBound); @@ -245,7 +251,8 @@ public static T unmarshal(final String xmlData, final Class... classesToB * @deprecated Use method {@link #unmarshal(Unmarshaller, String)} together with {@link UnmarshallerBuilder} instead */ @Deprecated - public static T unmarshal(final String xmlData, final XmlAdapter[] adapters, final Class... classesToBeBound) { + @Nullable + public static T unmarshal(@Nullable final String xmlData, final XmlAdapter[] adapters, final Class... classesToBeBound) { final UnmarshallerBuilder builder = new UnmarshallerBuilder(); if (classesToBeBound != null) { builder.addClassesToBeBound(classesToBeBound); @@ -273,7 +280,8 @@ public static T unmarshal(final String xmlData, final XmlAdapter[] ada * @deprecated Use method {@link #unmarshal(Unmarshaller, String)} together with {@link UnmarshallerBuilder} instead */ @Deprecated - public static T unmarshal(final JAXBContext ctx, final String xmlData, final XmlAdapter[] adapters) { + @Nullable + public static T unmarshal(final JAXBContext ctx, @Nullable final String xmlData, final XmlAdapter @Nullable [] adapters) { final UnmarshallerBuilder builder = new UnmarshallerBuilder().withContext(ctx); if (adapters != null) { builder.addAdapters(adapters); @@ -300,7 +308,7 @@ public static T unmarshal(final JAXBContext ctx, final String xmlData, final * @deprecated Use method {@link #unmarshal(Unmarshaller, Reader)} together with {@link UnmarshallerBuilder} instead */ @Deprecated - public static T unmarshal(final JAXBContext ctx, final Reader reader, final XmlAdapter[] adapters) { + public static T unmarshal(final JAXBContext ctx, final Reader reader, final XmlAdapter @Nullable [] adapters) { final UnmarshallerBuilder builder = new UnmarshallerBuilder().withContext(ctx); if (adapters != null) { builder.addAdapters(adapters); @@ -323,7 +331,8 @@ public static T unmarshal(final JAXBContext ctx, final Reader reader, final * Type returned. */ @SuppressWarnings("unchecked") - public static T unmarshal(final Unmarshaller unmarshaller, final String xml) { + @Nullable + public static T unmarshal(final Unmarshaller unmarshaller, @Nullable final String xml) { Utils4J.checkNotNull("unmarshaller", unmarshaller); if (xml == null) { return null; @@ -397,7 +406,8 @@ public static T unmarshal(final Unmarshaller unmarshaller, final File file) * @param * Type of the data. */ - public static String marshal(final Marshaller marshaller, final T data) { + @Nullable + public static String marshal(final Marshaller marshaller, @Nullable final T data) { Utils4J.checkNotNull("marshaller", marshaller); if (data == null) { return null; @@ -420,7 +430,7 @@ public static String marshal(final Marshaller marshaller, final T data) { * @param * Type of the data to write. */ - public static void marshal(final Marshaller marshaller, final T data, final Writer writer) { + public static void marshal(final Marshaller marshaller, @Nullable final T data, final Writer writer) { Utils4J.checkNotNull("marshaller", marshaller); Utils4J.checkNotNull("writer", writer); if (data == null) { @@ -446,7 +456,7 @@ public static void marshal(final Marshaller marshaller, final T data, final * @param * Type of the data to write. */ - public static void marshal(final Marshaller marshaller, final T data, final XMLStreamWriter writer) { + public static void marshal(final Marshaller marshaller, @Nullable final T data, final XMLStreamWriter writer) { Utils4J.checkNotNull("marshaller", marshaller); Utils4J.checkNotNull("writer", writer); if (data == null) { diff --git a/src/main/java/org/fuin/utils4j/jaxb/MarshallerBuilder.java b/src/main/java/org/fuin/utils4j/jaxb/MarshallerBuilder.java index 00c17c1..86c99ae 100644 --- a/src/main/java/org/fuin/utils4j/jaxb/MarshallerBuilder.java +++ b/src/main/java/org/fuin/utils4j/jaxb/MarshallerBuilder.java @@ -3,6 +3,7 @@ import jakarta.xml.bind.*; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import org.fuin.utils4j.Utils4J; +import org.jspecify.annotations.Nullable; import org.xml.sax.SAXException; import javax.xml.XMLConstants; @@ -25,10 +26,12 @@ public class MarshallerBuilder { private final Map properties; - private Marshaller.Listener listener; + private Marshaller.@Nullable Listener listener; + @Nullable private ValidationEventHandler handler; + @Nullable private JAXBContext ctx; diff --git a/src/main/java/org/fuin/utils4j/jaxb/UnmarshallerBuilder.java b/src/main/java/org/fuin/utils4j/jaxb/UnmarshallerBuilder.java index b16cc7c..fbe6260 100644 --- a/src/main/java/org/fuin/utils4j/jaxb/UnmarshallerBuilder.java +++ b/src/main/java/org/fuin/utils4j/jaxb/UnmarshallerBuilder.java @@ -3,6 +3,7 @@ import jakarta.xml.bind.*; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import org.fuin.utils4j.Utils4J; +import org.jspecify.annotations.Nullable; import org.xml.sax.SAXException; import javax.xml.XMLConstants; @@ -25,11 +26,13 @@ public final class UnmarshallerBuilder { private final Set> classesToBeBound; + @Nullable private ValidationEventHandler handler; + @Nullable private JAXBContext ctx; - private Unmarshaller.Listener listener; + private Unmarshaller.@Nullable Listener listener; /** * Default constructor. diff --git a/src/main/java/org/fuin/utils4j/jaxb/package-info.java b/src/main/java/org/fuin/utils4j/jaxb/package-info.java new file mode 100644 index 0000000..a48b0c1 --- /dev/null +++ b/src/main/java/org/fuin/utils4j/jaxb/package-info.java @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2015 Michael Schnell. All rights reserved. + * http://www.fuin.org/ + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see http://www.gnu.org/licenses/. + */ + +/** + * Convenience builders and helpers for working with Jakarta XML Binding (JAXB). + */ +@NullMarked +package org.fuin.utils4j.jaxb; + +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/fuin/utils4j/package-info.java b/src/main/java/org/fuin/utils4j/package-info.java index b4748d6..85bbaf3 100644 --- a/src/main/java/org/fuin/utils4j/package-info.java +++ b/src/main/java/org/fuin/utils4j/package-info.java @@ -19,4 +19,7 @@ /** * Basic Java utilities. */ +@NullMarked package org.fuin.utils4j; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/fuin/utils4j/Utils4JTest.java b/src/test/java/org/fuin/utils4j/Utils4JTest.java index 8893331..805ab2e 100644 --- a/src/test/java/org/fuin/utils4j/Utils4JTest.java +++ b/src/test/java/org/fuin/utils4j/Utils4JTest.java @@ -37,6 +37,7 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; +import java.util.UUID; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -1002,6 +1003,28 @@ public final void testRunLockedFails() { } + @Test + public void testAsShortStringAndParse() { + + // Single example + final UUID uuid1 = UUID.fromString("c9a5ab65-0fae-4ff8-8858-e06ddaf595e7"); + final String str = Utils4J.uuid2ShortStr(uuid1); + // MSB/LSB Base64 encoded would be: "yaWrZQ+uT/iIWOBt2vWV5w" + assertThat(str).isEqualTo( "yaWrZQ-uT_iIWOBt2vWV5w"); + final UUID uuid2 = Utils4J.shortStr2uuid(str); + assertThat(uuid2).isEqualTo(uuid1); + + // Some random values + for (int i= 0; i < 10000; i++) { + final UUID original = UUID.randomUUID(); + final String shortStr =Utils4J.uuid2ShortStr(original); + final UUID copy = Utils4J.shortStr2uuid(shortStr); + assertThat(copy).isEqualTo(original); + } + + } + + private static class MyTestClass { private String name;