Bilbo Baggins
Bilbo Baggins

Reputation: 3019

NullPointer exception while generating code using swagger codegen for openapi 3.0

Facing issue of NullPointer while generating code through swagger codegen. I am new to gradle and have updated build.gradle to reflect latest changes required to generate the code for the given API spec.

Please provide your valuable inputs.

My build.gradle is as shown below:

import io.swagger.codegen.v3.CodegenConfigLoader
import io.swagger.codegen.v3.DefaultGenerator
import io.swagger.codegen.v3.ClientOptInput
import io.swagger.codegen.v3.ClientOpts
import io.swagger.v3.parser.OpenAPIV3Parser

buildscript {
    ext {
        springBootVersion = '2.0.5.RELEASE'
        jacocoWorkspaceDirectory = "/jacoco/"
    }
    repositories {
        mavenLocal()
        maven { url "http://repo.maven.apache.org/maven2" }
    }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
        classpath group: "io.spring.gradle", name: "dependency-management-plugin", version: "1.0.6.RELEASE"
        classpath 'co.bambo.sonar:bambo-sonar-gradle-plugin:2.7.1.RELEASE'
        classpath group: "org.unbroken-dome.gradle-plugins", name: "gradle-testsets-plugin", version: "1.4.2"
        classpath('io.swagger.codegen.v3:swagger-codegen-maven-plugin:3.0.16')
        classpath("io.swagger.core.v3:swagger-core:2.1.1")
    }
}

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'maven-publish'
apply plugin: "io.spring.dependency-management"
apply plugin: "org.springframework.boot"
apply plugin: 'bambo-sonar'
apply plugin: "jacoco"
apply plugin: "findbugs"
apply plugin: "checkstyle"
apply plugin: "pmd"
apply plugin: 'org.unbroken-dome.test-sets'


project.buildDir = 'target'

ext {
    appName = 'school-management'
    apiPackage = 'co.bambo.school.management.generated.api'
    modelPackage = 'co.bambo.school.management.generated.model'
    swaggerFile = "${rootDir}/src/main/resources/schoolmanagement.v1.yaml"
    swaggerBuildDir = "${project.buildDir}/" + appName
}


// NOTE :-> Following are to be included as and when required.
// These variables are defined for the purpose of exclusion from the sonar scan. Packages mentioned will be exluded
// from Sonar Scans.
def excludeschoolmanagementConstants = "src/main/java/co/bambo/school/management/constants/schoolmanagementConstants.java"
def excludeCaasConstants = "src/main/java/co/bambo/school/management/pcf/constants/CaasConstants.java"

sourceCompatibility = 1.8
targetCompatibility = 1.8

group = 'co.bambo.schoolmanagement'
version = '1.0.0-SNAPSHOT'

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

repositories {
    mavenLocal()
    maven { url "http://repo.maven.apache.org/maven2" }
}

configurations {
    testUtilCompile.extendsFrom testImplementation
    testUtilRuntime.extendsFrom testRuntime
}

sourceSets {
    testUtil {
        java {
            srcDir 'src/test-util/java'
        }
    }
}

testSets {
    funcTest { dirName = "func-test" }
}


// PLUGIN CONFIGs
springBoot {
    mainClassName = 'co.bambo.school.management.schoolmanagementApplication'
    buildInfo()
}

bootJar {
    baseName = 'school-management'
    destinationDir = project.buildDir
    archiveName = 'school-management.jar'
}

// FIXME - IN FUTURE WE NEED TO MAKE THIS to Zero.
checkstyle {
    maxErrors = 70
    maxWarnings = 70
    toolVersion = 8.17
}

[checkstyleMain, checkstyleTest].each { task ->
    task.logging.setLevel(LogLevel.LIFECYCLE)
    // FIXME -  This is to be updated with False.
    task.ignoreFailures = true
}

findbugs {
    toolVersion = "3.0.1"
    sourceSets = [sourceSets.main]
    // FIXME - Make this false.
    ignoreFailures = true
    effort = "max"
    reportLevel = "medium"
    excludeFilter = file("$rootDir/config/findbugs/exclude.xml")
}

pmd {
    toolVersion = "5.6.1"
    ignoreFailures = true
    sourceSets = [sourceSets.main]
    reportsDir = file("${project.buildDir}/reports/pmdTest")
    ruleSets = [
            'java-basic'
    ]
}

tasks.withType(FindBugs) {
    reports {
        xml.enabled = false
        html.enabled = true
    }
}

tasks.withType(Pmd) {
    reports {
        xml.enabled = false
        html.enabled = true
    }
}


sonarqube {
    properties {
        property "sonar.projectName", "school-management"
        property "sonar.projectKey", "school-management"
        property "sonar.jacoco.reportPath", "build/jacoco/test.exec"
        property "sonar.junit.reportPaths", "build/test-results/test"
        property "sonar.host.url", "https://fusion.bambo.int/sonar"
        property "sonar.gateId", "307"
        property "sonar.projectDescription", "school management microservice."
        property "sonar.skip.qualityGate", "false"
        property "sonar.exclusions", [excludeschoolmanagementConstants, excludeCaasConstants]
    }
}

// Actual task for generating the server
task generateServer {
    doLast {
        def openAPI = new OpenAPIV3Parser().read(rootProject.swaggerFile.toString(), null, null)
        def clientOptInput = new ClientOptInput().openAPI(openAPI)
        def codeGenConfig = CodegenConfigLoader.forName('spring')
        codeGenConfig.setApiPackage(rootProject.ext.apiPackage)            // Package to be used for the API interfaces
        codeGenConfig.setModelPackage(rootProject.ext.modelPackage)        // Package to be used for the API models
        codeGenConfig.setInputSpec(rootProject.ext.swaggerFile.toString()) // The swagger API file
        codeGenConfig.setOutputDir(rootProject.ext.swaggerBuildDir)
        // The output directory, user-service-contract/build/user-service-server/
//        codegenConfig.addSystemProperty("models", "");
//        codegenConfig.addSystemProperty("apis", "");

        def clientOps = new ClientOpts()
        clientOps.setProperties([
                'dateLibrary'  : 'java8', // Date library to use
                'useTags'      : 'true',  // Use tags for the naming
                'interfaceOnly': 'false'// Generating the Controller API interface and the models only
        ])
        clientOptInput.setOpts(clientOps)
        def generator = new DefaultGenerator().opts(clientOptInput)
        generator.generate() // Executing the generation
    }
}

compileJava {
    dependsOn generateServer
}

build {
    dependsOn generateServer
}

// ---- PLUGIN CONFIG ENDs


dependencyManagement {
    imports {
        mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
    }
}

funcTest {
    doFirst {
        jacoco {
            destinationFile = file("${buildDir}" + jacocoWorkspaceDirectory + "test.exec")
        }
    }

    dependsOn cleanTest
    dependsOn compileTestUtilJava
    dependsOn cleanJacocoTestReport
    dependsOn cleanJacocoTestCoverageVerification
    finalizedBy jacocoTestReport
    finalizedBy jacocoTestCoverageVerification
    environment SPRING_PROFILES_ACTIVE: environment.SPRING_PROFILES_ACTIVE ?: "local"
}

dependencies {
    // Spring specific dependencies
    implementation('org.springframework:spring-tx')
    implementation('org.springframework:spring-core')
    implementation('org.springframework:spring-context')
    implementation('org.springframework:spring-beans')
    implementation('org.springframework:spring-expression')
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
    implementation group: 'org.springframework.retry', name: 'spring-retry'

    implementation group: 'org.springframework.boot', name: "spring-boot-starter-data-jpa"

    // Spring cloud
    // Upgraded springcloud-starter-vault from 2.0.0.RC1 to 2.0.1.RELEASE.
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-vault-config', version: '2.0.1.RELEASE'
    implementation group: 'io.pivotal.spring.cloud', name: 'spring-cloud-services-starter-config-client', version: '2.0.1.RELEASE'
    // Upgraded "spring-cloud-starter-bus-amqp" following from 2.0.0.RC1 to 2.0.1.RELEASE
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bus-amqp', version: '2.0.1.RELEASE'

    // SWagger dependencies
    implementation group: 'io.swagger', name: 'swagger-parser', version: '1.0.23'
    implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
    implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
    implementation group: 'com.atlassian.oai', name: 'swagger-request-validator-core', version: '1.3.9'

    // Apache - Commons
    implementation group: 'commons-io', name: 'commons-io', version: '2.6'

    // Oracle dependencies
    implementation group: 'com.oracle', name: 'ojdbc7', version: '12.1.0'

    //2nd level cache dependency
    implementation group: 'org.hibernate', name: 'hibernate-ehcache'

    // Logback
    implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
    implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3'

    implementation group: 'org.apache.camel', name: 'camel-jsonpath', version: '2.22.0'
    implementation group: 'com.google.gdata', name: 'core', version: '1.47.1'
    implementation group: 'com.google.guava', name: 'guava', version: '20.0'

    // FIXME - Eventually phase - out this Mapper
    implementation group: 'ma.glasnost.orika', name: 'orika-core', version: '1.5.4'

    // JSON
    implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-xml', version: '2.8.10'
    implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'

    // Mapstruct
    implementation 'org.mapstruct:mapstruct:1.3.0.Final'
    implementation 'org.mapstruct:mapstruct-processor:1.3.0.Final'

    implementation group: 'ch.qos.logback.contrib', name: 'logback-json-classic', version: '0.1.5'
    implementation group: 'ch.qos.logback.contrib', name: 'logback-jackson', version: '0.1.5'

    testImplementation "junit:junit:4.12"
    testImplementation group: "org.springframework.boot", name: "spring-boot-starter-test"
    testImplementation group: 'com.github.tomakehurst', name: 'wiremock', version: '2.21.0'

    testUtilCompile sourceSets.main.output
    funcTestCompile sourceSets.testUtil.output
}

I am facing issue of NullPointer on this line def generator = new DefaultGenerator().opts(clientOptInput).

Update-1 I understand the question seems fairly easy for NullPointer exception in java. but trust me, I am not able to figure out in build.gradle why it is failing as it is not even showing proper error message that what is missing. Below shown message is all I am getting. Even the debugging of groovy script isn't helping me.

Execution failed for task ':generateServer'.
> java.lang.NullPointerException (no error message)

Update-2 I just got to see in the DefaultGenerator.java on line number 77, found this line. This is where I am getting NullPointerException this.config.additionalProperties().putAll(opts.getOpts().getProperties());

I am not sure what is missing in parameters. I am passing opts and getOpts() is also populating properties file.

Using gradle version 4.9.

Upvotes: 4

Views: 8823

Answers (1)

Bilbo Baggins
Bilbo Baggins

Reputation: 3019

Okay I figured it out, it was a silly mistake in understanding and in code as well.

I got to know this mistake just after debugging and browsing through the swagger-codegen code. I realized that the config is required to generate code, based on this config specified it is generating code. I was generating the spring based CodeGenConfiguration but I was not passing it into the clientOptInput. My understanding was wrong, I assumed it will load the class for CodeGenConfig (Which it does load) and that's it. however you also need to pass it to the clientOptInput variable.

def codeGenConfig = CodegenConfigLoader.forName('spring')
clientOptInput.setConfig(codeGenConfig)

Since, the code for DefaultGenerator.java as per below:

    @Override
    public Generator opts(ClientOptInput opts) {
        this.opts = opts;
        this.openAPI = opts.getOpenAPI();
        this.config = opts.getConfig();

I need to set it to the ClientOptInput instance. This is the reason. Posting for others like me who makes mistakes :) :)

Upvotes: 3

Related Questions