Jib: Gradle build fails to retrieve image with ClassCastException in HTTP code

Created on 20 Sep 2018  路  4Comments  路  Source: GoogleContainerTools/jib

Description of the issue:
I made a fairly minimal configuration for this trellis-ext-db project: https://github.com/trellis-ldp/trellis-ext-db

When I run it I get a ClassCastException in the HTTP layer, where the Apache HTTP client is trying to cast a Long value into an Integer as part of the request configuration. Looks like an API mismatch. Sorry if I am missing something obvious to others.

Expected behavior:
Jib should download the 'openjdk:8-slim' baseimage from DockerHub.

Steps to reproduce:
1) Check out the trellis-ext-db project
2) Replace the build.gradle files with the content below
3) Run command: ./gradlew build
4) Run command: ./gradlew jibDockerBuild --stacktrace

Environment:
Ubuntu 16
Gradle 4.8.1

jib-gradle-plugin Configuration:


build.gradle (click the arrow to expand for details)

apply plugin: 'application'
apply plugin: 'distribution'
apply plugin: 'nebula.ospackage-application'

description = 'Trellis Database Application'
mainClassName = 'org.trellisldp.ext.app.db.TrellisApplication'
applicationName = 'trellis-db'

dependencies {
    compile project(':trellis-db-app')

    runtime("javax.xml.bind:jaxb-api:$jaxbVersion")
}

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.ajoberstar:grgit:1.1.0'
    }
}

// Get commit id of HEAD.
apply plugin: 'java'
apply plugin: 'application'
ext {
    git = org.ajoberstar.grgit.Grgit.open(file('../.'))
    revision = git.head().id
}

ospackage {
    packageName = 'trellis-db'
    release = '1'
    os = LINUX
    license = 'ASL 2.0'
    url = 'https://www.trellisldp.org'
    summary = 'Trellis is a linked data server.'
    packageDescription = '''\
Trellis is a linked data server that can be used for storing large volumes of content,
particularly in cases where that content is stored for years and decades.'''

    user = 'trellis'
    // Note: the linux installers do not depend on Java because it is too complicated to
    // reliably navigate this dependency across linux distributions and supported JRE versions.
    requires('systemd')
    into '/opt/trellis'

    from ('scripts') {
        into 'bin'
        fileMode = 0550
    }

    from ('jar.outputs.files') {
        into 'lib'
    }

    from ('src/dist/etc') {
        fileType CONFIG | NOREPLACE
        into '/opt/trellis/etc'
    }

    from ('src/dist/data') {
        fileType CONFIG | NOREPLACE
        into '/opt/trellis/data'
    }
}

buildRpm {
    preInstall file('src/rpm/preInstall.sh')
    postInstall file('src/rpm/postInstall.sh')
    preUninstall file('src/rpm/preUninstall.sh')
}

buildDeb {
    preInstall file('src/deb/preInstall.sh')
    postInstall file('src/deb/postInstall.sh')
    preUninstall file('src/deb/preUninstall.sh')
}

jib {
    extraDirectory = file('src/docker')
    from {
      image = 'openjdk:8-jdk'
    }
    to {
      image = 'gregjan/trellis-extdb'
      credHelper = 'docker-credential-*'
    }
    container {
      labels = ['git.url':'https://github.com/trellis-ldp/trellis-ext-db', 'git.commit':revision]
      mainClass = mainClassName
      args = ['server', '/config.yml']
      ports = ['80']
    }
}


signing {
    sign buildRpm
    sign buildDeb
    sign distTar
    sign distZip
}

test {
    jacoco {
        enabled = false;
    }
}

task copyDistTask(type: Copy) {
    from '../LICENSE'
    from 'README.md'
    into 'src/dist'
}

assembleDist {
    dependsOn copyDistTask
    dependsOn buildRpm
    dependsOn buildDeb
    dependsOn signBuildRpm
    dependsOn signBuildDeb
}

The file above is in a sub-project called 'deployment'. The main build.gradle file looks like this:

plugins {
    id 'com.github.hierynomus.license' version '0.14.0'
    id 'com.github.ben-manes.versions' version '0.20.0'
    id 'net.researchgate.release' version '2.7.0'
    id 'com.github.kt3k.coveralls' version '2.8.2'
    id 'org.sonarqube' version '2.6.2'
    id 'nebula.ospackage' version '4.9.3'
    id 'org.owasp.dependencycheck' version '3.2.1'
    id 'com.google.cloud.tools.jib' version '0.9.10'
}

ext {

    /* Dependencies */
    activationVersion = '1.1.1'
    jdbiVersion = '3.3.0'
    commonsLangVersion = '3.7'
    commonsRdfVersion = '0.5.0'
    kafkaVersion = '1.1.0'
    trellisVersion = '0.7.1'
    mysqlVersion = '8.0.11'
    javaxInjectVersion = '2.5.0-b61'
    tamayaVersion = '0.3-incubating'
    slf4jVersion = '1.7.25'
    activeMqVersion = '5.15.4'
    jmsApiVersion = '2.0.1'
    dropwizardVersion = '1.3.5'
    snakeyamlVersion = '1.21'

    /* Databases */
    mysqlVersion = '8.0.11'
    postgresVersion = '42.2.3'

    /* Testing */
    activationVersion = '1.1.1'
    apiguardianVersion = '1.0.0'
    checkstyleVersion = '8.10'
    commonsTextVersion = '1.4'
    guavaVersion = '25.1-jre'
    h2Version = '1.4.197'
    jaxbVersion = '2.3.0'
    jacocoVersion = '0.8.1'
    junitPlatformVersion = '1.2.0'
    junitVersion = '5.2.0'
    liquibaseVersion = '3.6.2'
    logbackVersion = '1.2.3'
    mockitoVersion = '2.19.0'
    otjPgVersion = '0.12.0'

    /* OSGi */
    projectOsgiVersion = project.version.replaceAll("-SNAPSHOT", ".SNAPSHOT")
}

allprojects { subproj ->
    apply plugin: 'java-library'
    apply plugin: 'maven-publish'
    apply plugin: 'signing'
    apply plugin: 'checkstyle'
    apply plugin: 'com.github.hierynomus.license'
    apply plugin: 'jacoco'

    ext {
        vendor = 'Trellis LDP'
        homepage = 'https://www.trellisldp.org'
        docURL = 'https://trellis-ldp.github.io/trellis/apidocs/'
        license = 'Apache 2'
    }

    jacoco.toolVersion = jacocoVersion

    group = 'org.trellisldp.ext'

    repositories {
        mavenCentral()
        jcenter()
        mavenLocal()
    }

    dependencies {
        testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
        testImplementation("org.apiguardian:apiguardian-api:${apiguardianVersion}")
        testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
    }

    gradle.projectsEvaluated {
        tasks.withType(JavaCompile) {
            options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
        }
    }

    release {
        tagTemplate = '$name-$version'
        git {
            requireBranch = 'master'
            signTag = true
        }
    }
}

subprojects { subproj ->

    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    jar {
        from("$rootDir/LICENSE") {
            into "META-INF"
        }
    }

    checkstyle {
        configFile = rootProject.file('buildtools/src/main/resources/checkstyle/checkstyle.xml')
        configProperties.checkstyleConfigDir = rootProject.file('buildtools/src/main/resources/checkstyle/')
        toolVersion = checkstyleVersion
    }

    task javadocJar(type: Jar) {
        classifier 'javadoc'
        from("$rootDir/LICENSE") {
            into "META-INF"
        }
        from javadoc
    }

    task sourceJar(type: Jar) {
        classifier 'sources'
        from("$rootDir/LICENSE") {
            into "META-INF"
        }
        from sourceSets.main.allSource
    }

    task processConfig(type: Copy) {
        from('src/main/cfg') {
            include '**/*.cfg'
        }
        into 'build/cfg/main'
    }

    classes {
        classes.dependsOn processConfig
    }

    artifacts {
        archives javadocJar
        archives sourceJar
    }

    license {
        include "**/*.java"
        header rootProject.file('buildtools/src/main/resources/license/HEADER.txt')
        strictCheck true
        mapping {
            java = 'SLASHSTAR_STYLE'
        }
    }

    publishing {
        publications {
            mavenJava(MavenPublication) {
                pom {
                    packaging = 'jar'
                    name = 'Trellis Linked Data Server: database extension'
                    description = 'A database persistence layer for the Trellis linked data server'
                    url = "https://www.trellisldp.org"
                    inceptionYear = '2017'

                    organization {
                        name = project.vendor
                        url = project.homepage
                    }

                    developers {
                        developer {
                            id = 'acoburn'
                            name = 'Aaron Coburn'
                            email = 'acoburn (at) apache (dot) org'
                        }
                    }

                    scm {
                        connection = 'scm:git:git://github.com/trellis-ldp/trellis-ext-db.git'
                        developerConnection = 'scm:git:[email protected]/trellis-ldp/trellis-ext-db.git'
                        url = 'https://github.com/trellis-ldp/trellis-ext-db'
                        tag = 'HEAD'
                    }

                    licenses {
                        license {
                            name = 'Apache License, Version 2.0'
                            url = 'http://www.apache.org/licenses/LICENSE-2.0'
                            comments = 'Copyright (c) 2017-2018 Trellis LDP'
                        }
                    }
                }
                pom.withXml {
                    // eliminate test-scoped dependencies
                    asNode().dependencies.removeAll { dep -> dep.scope == "test" }
                }

                from components.java

                artifact(sourceJar) {
                    classifier = 'sources'
                }

                artifact(javadocJar) {
                    classifier = 'javadoc'
                }
            }
        }
        repositories {
            maven {
                def sonatypeUsername = project.hasProperty('ossrhUsername') ? ossrhUsername : ""
                def sonatypePassword = project.hasProperty('ossrhPassword') ? ossrhPassword : ""
                if (version.endsWith("SNAPSHOT")) {
                    url "https://oss.sonatype.org/content/repositories/snapshots/"
                } else {
                    url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
                }
                credentials {
                    username sonatypeUsername
                    password sonatypePassword
                }
            }
        }
    }

    tasks.withType(PublishToMavenRepository) {
        onlyIf {
            subproj.name != "trellis-db-deployment"
        }
    }
    tasks.withType(PublishToMavenLocal) {
        onlyIf {
            subproj.name != "trellis-db-deployment"
        }
    }
    task install(dependsOn: [assemble, publishToMavenLocal])
    task upload(dependsOn: [assemble, publish])

    processResources {
        outputs.upToDateWhen { false }
        filesMatching(['**/features.xml', '**/banner.txt']) {
            expand project.properties
        }
    }

    signing {
        required {
            !version.toString().endsWith('-SNAPSHOT') && tasks.withType(PublishToMavenRepository).find {
                gradle.taskGraph.hasTask it
            }
        }
        sign publishing.publications
    }

    task docs(type: Javadoc) {
        outputs.upToDateWhen { false }
        source sourceSets.main.allJava
        classpath = files(sourceSets.main.compileClasspath)
        destinationDir = new File(projectDir, "docs/${version}")
        options {
            links "https://docs.oracle.com/javase/8/docs/api/"
            links 'https://docs.oracle.com/javaee/7/api/'
            links 'https://trellis-ldp.github.io/trellis/apidocs/'
            links 'https://commons.apache.org/proper/commons-lang/javadocs/api-3.7/'
            links 'https://commons.apache.org/proper/commons-rdf/apidocs/'
            links 'https://www.dropwizard.io/1.3.0/dropwizard-core/apidocs/'
        }
    }

    sonarqube {
        skipProject = JavaVersion.current().isJava10Compatible()
    }

    test {
        useJUnitPlatform()
        jacoco {
            enabled = ! JavaVersion.current().isJava11Compatible()
        }
    }

    afterReleaseBuild.dependsOn docs
    afterReleaseBuild.dependsOn publish

    jacoco {
        toolVersion = jacocoVersion
    }

    jacocoTestReport {
        reports {
            xml.enabled = true
            html.enabled = true
        }
    }
}

configure(rootProject) {

    task apidocs(type: Javadoc, dependsOn: getTasksByName('docs', true)) {
        outputs.upToDateWhen { false }
        destinationDir = new File(projectDir, "docs/apidocs")
        title = "Trellis Linked Data Server Documentation"
        exclude '**/impl/*'
        exclude '**/*Tests.java'
        options {
            memberLevel = JavadocMemberLevel.PUBLIC
            links "https://docs.oracle.com/javase/8/docs/api/"
            links 'https://docs.oracle.com/javaee/7/api/'
            links 'https://trellis-ldp.github.io/trellis/apidocs/'
            links 'https://commons.apache.org/proper/commons-lang/javadocs/api-3.7/'
            links 'https://commons.apache.org/proper/commons-rdf/apidocs/'
            links 'https://www.dropwizard.io/1.3.0/dropwizard-core/apidocs/'
        }

        source subprojects.collect { project -> project.sourceSets.main.allJava }
        classpath = files(subprojects.collect { project -> project.sourceSets.main.compileClasspath })
    }

    sonarqube {
        properties {
            property "sonar.projectName", "Trellis Server: Database Extension"
            property "sonar.projectKey", "org.trellisldp:trellis-ext-db"
            property "sonar.links.homepage", "https://www.trellisldp.org"
            property "sonar.links.issue", "https://github.com/trellis-ldp/trellis-ext-db/issues"
            property "sonar.links.scm_dev", "scm:git:[email protected]:trellis-ldp/trellis-ext-db.git"
        }
    }

    // Ignore alpha, beta, milestone and release candidates
    dependencyUpdates.resolutionStrategy = {
        componentSelection { rules ->
            rules.all { ComponentSelection selection ->
                boolean rejected = ['alpha', 'beta', 'rc', 'm'].any { qualifier ->
                    selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
                }
                if (rejected) {
                    selection.reject("Release Candidate")
                }
            }
        }
    }

    task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
        dependsOn = subprojects.test
        additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs)
        sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs)
        classDirectories =  files(subprojects.sourceSets.main.output)
        executionData = files(subprojects.findAll { it.name != 'trellis-db-deployment' }.jacocoTestReport.executionData)
        reports {
            html.enabled = true
            xml.enabled = true
            csv.enabled = false
        }
    }

    coveralls {
        sourceDirs = subprojects.sourceSets.main.allSource.srcDirs.flatten()
        jacocoReportPath = "${buildDir}/reports/jacoco/jacocoRootReport/jacocoRootReport.xml"
    }

    tasks.coveralls {
        dependsOn 'jacocoRootReport'
    }
}

Log output:

Caused by: java.lang.ClassCastException: java.base/java.lang.Long cannot be cast to java.base/java.lang.Integer
        at org.apache.http.params.AbstractHttpParams.getIntParameter(AbstractHttpParams.java:70)
        at org.apache.http.client.params.HttpClientParamConfig.getRequestConfig(HttpClientParamConfig.java:54)
        at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:806)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
        at com.google.api.client.http.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:65)
        at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:981)
        at com.google.cloud.tools.jib.http.Connection.send(Connection.java:161)
        at com.google.cloud.tools.jib.registry.RegistryEndpointCaller.call(RegistryEndpointCaller.java:227)
        at com.google.cloud.tools.jib.registry.RegistryEndpointCaller.callWithAllowInsecureRegistryHandling(RegistryEndpointCaller.java:150)
        at com.google.cloud.tools.jib.registry.RegistryEndpointCaller.call(RegistryEndpointCaller.java:140)
        at com.google.cloud.tools.jib.registry.RegistryClient.callRegistryEndpoint(RegistryClient.java:356)
        at com.google.cloud.tools.jib.registry.RegistryClient.pullManifest(RegistryClient.java:226)
        at com.google.cloud.tools.jib.registry.RegistryClient.pullManifest(RegistryClient.java:234)
        at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.pullBaseImage(PullBaseImageStep.java:197)
        at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.call(PullBaseImageStep.java:116)
        at com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.call(PullBaseImageStep.java:57)
        at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:127)
        at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:57)
        at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:80)

All 4 comments

Our Jib dev environment uses Google HTTP Client 1.23.0. Checking the dependency graph on jib-core with ./gradlew dependencies --configuration compile shows that it pulls in Apache HttpClient 4.0.1:

compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
+--- com.google.http-client:google-http-client:1.23.0
|    +--- com.google.code.findbugs:jsr305:1.3.9
|    \--- org.apache.httpcomponents:httpclient:4.0.1

However, when executing Jib on @gregjan's repo, I think Jib pulls in and uses Apache HttpClient 4.3. The source files of 4.3 match the stack trace.

And it looks like Google HTTP Client 1.23.0 does not work well with Apache HttpClient 4.3. Below is a sample project that I created to reproduce the same stack trace.

First, I declare both Google HTTP Client 1.23.0 and Apache HttpClient 4.3:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>test-project</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.http-client</groupId>
      <artifactId>google-http-client</artifactId>
      <version>1.23.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpcore</artifactId>
      <version>4.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.3</version>
    </dependency>
  </dependencies>
</project>

Main just tries a connection:

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.apache.ApacheHttpTransport;
import java.io.IOException;
import org.apache.http.client.ClientProtocolException;

public class Main {

  public static void main(String[] args) throws ClientProtocolException, IOException {
    new ApacheHttpTransport().createRequestFactory().buildGetRequest(new GenericUrl("https://google.com")).execute();
  }
}

I get the same exception:

Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
    at org.apache.http.params.AbstractHttpParams.getIntParameter(AbstractHttpParams.java:70)
    at org.apache.http.client.params.HttpClientParamConfig.getRequestConfig(HttpClientParamConfig.java:54)
    at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:806)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
    at com.google.api.client.http.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:65)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:981)
    at Main.main(Main.java:9)

If I use Apache HttpClient 4.0.1 by updating pom.xml, it works well.

Is there a way to force using some JARs for a Gradle task execution environment (not affecting the actual dependencies of the project)?

The build script can be forced to use a certain dependency via the buildscript block. Add this to the top of build.gradle to force Gradle to use Apache HTTP client 4.0.1 with jib-gradle-plugin:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath('org.apache.httpcomponents:httpclient:4.0.1') {
            force = true
        }
    }
}

Many thanks! That worked for me.

Was this page helpful?
0 / 5 - 0 ratings