swagger-api / swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
http://swagger.io
Apache License 2.0
16.94k stars 6.03k forks source link

Failed to load class "org.slf4j.impl.StaticLoggerBinder exception when using 3.0.0 branch #12170

Open MeikandaNayanar opened 1 year ago

MeikandaNayanar commented 1 year ago
Description

I am using Windows 11 with jdk-11.0.4. I am trying to generate SDK project for my ASP.Net Core application using OpenAPI3.0. I am following the steps mentioned in this GitHub repo's read me. But ended up with the below exception.

PS D:\OpenAPI\v3.0.43_codegen\swagger-codegen-3.0.43\swagger-codegen-3.0.43> java -jar modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i D:\petstore.json -l php -o c:\temp\petstore_local_api_client
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation.
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Swagger-codegen version
Swagger declaration file content or url

Pet store same comes by default.

Command line used for generation

PS D:\OpenAPI\v3.0.43_codegen\swagger-codegen-3.0.43\swagger-codegen-3.0.43> java -jar modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i D:\petstore.json -l php -o c:\temp\petstore_local_api_client

Steps to reproduce

PS D:\OpenAPI\v3.0.43_codegen\swagger-codegen-3.0.43\swagger-codegen-3.0.43> java -jar modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i D:\petstore.json -l php -o c:\temp\petstore_local_api_client

Related issues/PRs

NA

Suggest a fix/enhancement

I tried the below.

  1. I have the slf4j-simple-2.0.7.jar and slf4j-api-2.0.7.jar in classpath of environment variable.
  2. I have the slf4j-simple-2.0.7.jar and slf4j-api-2.0.7.jar in D:\OpenAPI\v3.0.43_codegen\swagger-codegen-3.0.43\swagger-codegen-3.0.43 which is the root folder in powershell
  3. I have the slf4j-simple-2.0.7.jar and slf4j-api-2.0.7.jar in :\OpenAPI\v3.0.43_codegen\swagger-codegen-3.0.43\swagger-codegen-3.0.43\ modules\swagger-codegen-cli\target\ where the swagger-codegen-cli.jar is available.

Note There is no exception when I try with master source or2.x.x source of CodeGen. This issue is raised when I am trying to use CodeGen 3.X.X version of source. Since I am trying to adapt OpenAPI spec 3.0 can someone help me with this?

aethanol commented 1 year ago

Getting the same error up until 3.0.41

whampt commented 1 year ago

Same issue.

On windows, built newest stable version 3.0.43 to create C# code from OpenApi v3. Built with Maven v 3.9.2 using java v 20.0.1

During build of codegen I had the following

[WARNING] [WARNING] Plugin validation issues were detected in 6 plugin(s) [WARNING] [WARNING] org.apache.maven.plugins:maven-plugin-plugin:3.6.0 [WARNING] org.apache.maven.plugins:maven-compiler-plugin:3.10.1 [WARNING] org.apache.maven.plugins:maven-dependency-plugin:2.8 [WARNING] org.apache.maven.plugins:maven-shade-plugin:3.3.0 [WARNING] org.apache.maven.plugins:maven-resources-plugin:3.3.0 [WARNING] org.apache.maven.plugins:maven-war-plugin:3.3.2

whampt commented 1 year ago

@aethanol Which version 3.0xx did you not get the same error with? 3.0.41 and up? Which Maven version did you use? Trying v 3.0.41 now

My command used to reproduce was: java -jar C:\Users\whampt\swagger-codegen-3.0.43\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i c:\Users\whampt\openapi.json -l csharp -o C:\Users\whampt\auto

I cannot share the openapi.json file

yokrysty commented 1 year ago

add org.slf4j:slf4j-simple:1.7.36 as dependency

what i did to have a working version:

aethanol commented 1 year ago

@aethanol Which version 3.0xx did you not get the same error with? 3.0.41 and up? Which Maven version did you use? Trying v 3.0.41 now

My command used to reproduce was: java -jar C:\Users\whampt\swagger-codegen-3.0.43\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar generate -i c:\Users\whampt\openapi.json -l csharp -o C:\Users\whampt\auto

I cannot share the openapi.json file

Oh, I reversed that. It is working for versions up until 3.0.41 for me.

jeremydyoung commented 1 year ago

This seems to be a duplicate of https://github.com/swagger-api/swagger-codegen/issues/12135

I am working on providing a pull request.

hlovdal commented 1 year ago

And to automate the steps outlined by @yokrysty:

VERSION=3.0.46
UPSTREAM_JAR=https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/$VERSION/swagger-codegen-cli-$VERSION.jar
CLI_JAR="$(basename $UPSTREAM_JAR)"

if [[ ! -f "$CLI_JAR" ]]
then
        curl -Lo "$CLI_JAR" "$UPSTREAM_JAR"
fi

# Recent versions of swagger-codegen-cli has a dependency on a logger
# implementation that is not included in the jar bundle and prevents the
# cli from working. So download a usable logger implementation...

if [[ ! -f slf4j-simple-1.7.36.jar ]]
then
        ./download-slf4j-simple.sh slf4j-simple-1.7.36.jar .
fi

# ...however using it is not as simple as adding it to the class path
# because when using the `-jar` argument "the JAR file is the source of
# all user classes, and other user class path settings are ignored."
# (https://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/java.html#-jar).
# So we have to modify the jar file and add the logger files into it.

CLI_JAR_WITH_SLF4J=${CLI_JAR/swagger-codegen-cli-/swagger-codegen-cli-with-slf4j-}

if [[ ! -f "$CLI_JAR_WITH_SLF4J" ]]
then
        ./append-slf4j-files.sh . "$(basename "$CLI_JAR")" "$(basename "$CLI_JAR_WITH_SLF4J")" slf4j-simple-1.7.36.jar
fi

java -jar "$CLI_JAR_WITH_SLF4J" generate ...

with download-slf4j-simple.sh being

#!/bin/bash

set -euo pipefail

# Without slf4j present, swagger-codegen-cli will fail like
#
#     SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
#     SLF4J: Defaulting to no-operation (NOP) logger implementation
#     SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
#
# https://github.com/swagger-api/swagger-codegen/issues/12170
# https://github.com/swagger-api/swagger-codegen/issues/12135
# http://web.archive.org/web/20230919045403/https://www.slf4j.org/codes.html

if [[ $# -ne 2 ]]
then
    echo "Usage: $0 <jar-file> <destination_dir>" >&2
    echo "E.g. $0 slf4j-simple-1.7.36.jar ." >&2
    exit 1
fi

JAR_FILE=${1}                   # slf4j-simple-1.7.36.jar
DEST=${1/slf4j-simple-/slf4j-simple:}       # slf4j-simple:1.7.36.jar
ARTIFACT=${DEST/.jar/}              # slf4j-simple:1.7.36
VER=${ARTIFACT/.*:/}                # 1.7.36
VOL_DIR=${2}

export MSYS_NO_PATHCONV=1

# https://stackoverflow.com/a/68275307/23118
docker run -it --rm -v "$VOL_DIR":/build/source --net=host maven /bin/bash -c \
    "mvn dependency:get -DrepoUrl=https://repo1.maven.org/maven2 -Dmaven.repo.local=/build/.m2/repository -Dartifact=org.slf4j:$ARTIFACT -Ddest=$DEST && mv /build/.m2/repository/org/slf4j/slf4j-simple/$VER/$JAR_FILE /build/source"

and append-slf4j-files.sh:

#!/bin/bash

set -euo pipefail

if [[ $# -ne 4 ]]
then
    echo "Usage: $0 <directory> <source_jar> <destination_jar> <slf4j_jar>" >&2
    echo "E.g. $0 . swagger-codegen-cli-3.0.46.jar swagger-codegen-cli-with-slf4j-3.0.46.jar slf4j-simple-1.7.36.jar" >&2
    exit 1
fi

VOL_DIR=${1}
SRC_JAR=${2}
DST_JAR=${3}
SLF4J_JAR=${4}

export MSYS_NO_PATHCONV=1

# The relavant files from the slf4j-simple-*.jar file are `org/slf4j/impl/*.class`,
# so just copy the whole `org` top level directory.

# https://stackoverflow.com/a/68275307/23118
docker run -it --rm -v "$VOL_DIR":/build/source --net=host maven /bin/bash -c \
    "mkdir /tmp/work; cp /build/source/$SRC_JAR /tmp/work/$DST_JAR; cd /tmp/work; jar -x -f /build/source/$SLF4J_JAR; jar -u -f $DST_JAR org; mv $DST_JAR /build/source;"
yokrysty commented 1 year ago

@hlovdal i like the fact that you have a lot of comments but i don't like the number of scripts involved and the usage of docker i prefer a simple cross platform python script using only the built-in modules:

import sys
import os
import contextlib
import urllib.request
from io import BytesIO
from zipfile import ZipFile

if len(sys.argv) < 2:
    print('''
Usage:
    download_swagger_codegen_cli.py {swagger_codegen_version} {download_dir_path}?

    if download_dir_path is omitted, current directory will be used

Eg.:
    download_swagger_codegen_cli.py 3.0.46
    ''')
    sys.exit(0)

out_dir_path = sys.argv[2] if len(sys.argv) == 3 else os.path.dirname(os.path.abspath(__file__)) 

if not os.path.exists(out_dir_path) or not os.path.isdir(out_dir_path):
    print('output download directory does not exists')
    sys.exit(1)

swagger_codegen_ver = sys.argv[1]
swagger_codegen_url = f'https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/{swagger_codegen_ver}/swagger-codegen-cli-{swagger_codegen_ver}.jar'

print(f'[INFO] downloading swagger-codegen-cli {swagger_codegen_ver} jar file')

with contextlib.closing(urllib.request.urlopen(swagger_codegen_url)) as swagger_jar_res:
    swagger_jar_buf = BytesIO(swagger_jar_res.read())

    print(f'[INFO] download finished')
    print('[INFO] searching swagger-codegen-cli jar file for slf4j version')

    slf4j_ver = None
    with ZipFile(swagger_jar_buf, mode='r') as swagger_jar:
        pom_prop_content = swagger_jar.read('META-INF/maven/org.slf4j/slf4j-api/pom.properties').decode(encoding='utf-8')
        pom_prop_pairs = [kv.split('=') for kv in pom_prop_content.splitlines()]
        slf4j_ver = next((val for (prop, val) in pom_prop_pairs if prop == 'version'), None)
        print(f'[INFO] found slf4j version {slf4j_ver}')

    assert slf4j_ver

    print(f'[INFO] downloading slf4j {slf4j_ver} jar file')
    slf4j_url = f'https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/{slf4j_ver}/slf4j-simple-{slf4j_ver}.jar'
    with contextlib.closing(urllib.request.urlopen(slf4j_url)) as slf4j_jar_res:
        slf4j_jar_buf = BytesIO(slf4j_jar_res.read())
        print(f'[INFO] download finished')

        print(f'[INFO] patching swagger-codegen-cli jar to include slf4j')

        with ZipFile(swagger_jar_buf, mode='a') as swagger_jar:
            with ZipFile(slf4j_jar_buf, mode='r') as slf4j_jar:
                for entry_name in slf4j_jar.namelist():
                    if entry_name in swagger_jar.NameToInfo:
                        continue
                    print(f'[INFO] writing entry {entry_name}')
                    entry_data = slf4j_jar.read(entry_name)
                    swagger_jar.writestr(entry_name, entry_data)

    swagger_jar_buf.seek(0)

    out_file_path = os.path.join(out_dir_path, f'swagger-codegen-cli-{swagger_codegen_ver}.jar')

    with open(out_file_path, 'wb') as f:
        f.write(swagger_jar_buf.getvalue())

    print(f'[INFO] swagger-codegen-cli jar file written at {out_file_path}')

output:

C:\Users\KRYSTY\Downloads>download_swagger_codegen_cli.py 3.0.46
[INFO] downloading swagger-codegen-cli 3.0.46 jar file
[INFO] download finished
[INFO] searching swagger-codegen-cli jar file for slf4j version
[INFO] found slf4j version 1.7.36
[INFO] downloading slf4j 1.7.36 jar file
[INFO] download finished
[INFO] patching swagger-codegen-cli jar to include slf4j
[INFO] writing entry org/slf4j/impl/
[INFO] writing entry META-INF/maven/org.slf4j/slf4j-simple/
[INFO] writing entry org/slf4j/impl/OutputChoice$1.class
[INFO] writing entry org/slf4j/impl/OutputChoice$OutputChoiceType.class
[INFO] writing entry org/slf4j/impl/OutputChoice.class
[INFO] writing entry org/slf4j/impl/SimpleLogger.class
[INFO] writing entry org/slf4j/impl/SimpleLoggerConfiguration$1.class
[INFO] writing entry org/slf4j/impl/SimpleLoggerConfiguration.class
[INFO] writing entry org/slf4j/impl/SimpleLoggerFactory.class
[INFO] writing entry org/slf4j/impl/StaticLoggerBinder.class
[INFO] writing entry org/slf4j/impl/StaticMDCBinder.class
[INFO] writing entry org/slf4j/impl/StaticMarkerBinder.class
[INFO] writing entry META-INF/maven/org.slf4j/slf4j-simple/pom.xml
[INFO] writing entry META-INF/maven/org.slf4j/slf4j-simple/pom.properties
[INFO] swagger-codegen-cli jar file written at C:\Users\KRYSTY\Downloads\swagger-codegen-cli-3.0.46.jar
hlovdal commented 9 months ago

This was solved in 3.0.47 so I assume this issue can be closed.

Now I am back to running

#!/bin/bash

set -euo pipefail

banner() {
        echo
        echo "===== $* ====="
        echo
}

TOP_DIR="$(dirname "$(git rev-parse --absolute-git-dir)")"
VERSION=3.0.51

# Java 9 starts adding restrictions, causing errors like
# Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make public boolean java.util.Collections$EmptyMap.isEmpty() accessible: module java.base does not "opens java.util" to unnamed module @76c3e77a
# https://stackoverflow.com/a/41331704/23118
# https://dev.to/jjbrt/how-to-avoid-resorting-to-add-exports-and-add-opens-in-jdk-16-and-later-j3m
# https://stackoverflow.com/questions/41265266/how-to-solve-inaccessibleobjectexception-unable-to-make-member-accessible-m
JAVA="java --add-opens java.base/java.util=ALL-UNNAMED"

UPSTREAM_JAR=https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/$VERSION/swagger-codegen-cli-$VERSION.jar
CLI_JAR="$TOP_DIR"/utils/classes/"$(basename $UPSTREAM_JAR)"

cd "$TOP_DIR"

if [[ ! -f "$CLI_JAR" ]]
then
        curl -Lo "$CLI_JAR" "$UPSTREAM_JAR"
fi

# E.g.
# "@angular/core": "^11.2.8",
NG_VERSION=$(grep '"@angular/core"' package.json | cut -d'"' -f4 | tr -d '^~')

function run {
        banner "$1"
        $JAVA -jar "$CLI_JAR" generate --additional-properties ngVersion="${NG_VERSION}" -i "$2" -l typescript-angular -o "$3"
}

run service1 http://localhost:4201/swagger/v1/swagger.json src/app/services/backend/apis/service1/models/auto-generated
run service2 http://localhost:4202/swagger/v1/swagger.json src/app/services/backend/apis/service2/models/auto-generated
...