GoogleCloudPlatform / app-gradle-plugin

The library has moved to https://github.com/GoogleCloudPlatform/appengine-plugins/tree/main/app-gradle-plugin

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Google Cloud SDK Throwing NullPointerException While Initializing

heathen00 opened this issue · comments

Summary

Google Cloud App Engine Gradle plugin throwing NullPointerException when any Gradle task is executed. Not sure why. Current workaround, is to remove the plugin from my build.gradle file and deploy my website to Google Cloud using the Google SDK CLI command: gcloud app deploy ./build/libs/personal-website-undefined.jar.

Details

After I upgraded the version of Kubuntu on my laptop to 20.04.1, I noticed the behaviour that I could no longer build this project since the Google Cloud App Engine Gradle plugin kept throwing a NullPointerException while the plugin was initialising. It doesn't matter what Gradle task I execute the exception is always the same (from './gradlew --stacktrace properties'):

Caused by: java.lang.NullPointerException
        at com.google.cloud.tools.gradle.appengine.appyaml.AppEngineAppYamlPlugin.lambda$configureExtensions$0(AppEngineAppYamlPlugin.java:96)
        at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.lambda$run$0(DefaultListenerBuildOperationDecorator.java:152)
        at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:60)
...

It is a massive stack trace but no other code belonging to the Google Cloud App Engine Gradle Plugin is mentioned after the first line. The rest of the stack trace is just Gradle code.

Although I am no Gradle expert, my interpretation of looking at the code that is throwing the NPE, https://github.com/GoogleCloudPlatform/app-gradle-plugin/blob/master/src/main/java/com/google/cloud/tools/gradle/appengine/appyaml/AppEngineAppYamlPlugin.java, is it seems as though the code is assuming that the Jar plugin will set a "jar" Gradle property but it isn't. I see no mention of this property in the Gradle Jar plugin documentation, either: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html., not even as a deprecated property. Thus, I really would not expect this assumption to be valid.

My gradle.build file:

buildscript {
  repositories {
    mavenCentral()
  }

  dependencies {
    classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.3.0'
  }
}

plugins {
  id 'org.springframework.boot' version '2.3.1.RELEASE'
  id 'eclipse'
}

apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.google.cloud.tools.appengine'

group = 'com.nickmlanglois.site'
version = 'undefined'

java {
  sourceCompatibility = JavaVersion.VERSION_11
  targetCompatibility = JavaVersion.VERSION_11
}

eclipse {
  classpath {
    downloadSources = true
    downloadJavadoc = true
  }
}

sourceSets {
  test {
    java {
      srcDir 'src/test/java'
      srcDir 'src/integration_test/java'
    }   
  }
}

configurations {
        developmentOnly
        runtimeClasspath {
                extendsFrom developmentOnly
        }   
}

repositories {
        mavenCentral()
}

dependencies {
        developmentOnly 'org.springframework.boot:spring-boot-devtools'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
        implementation 'org.springframework.cloud:spring-cloud-gcp-starter-data-firestore:1.2.3.RELEASE'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation 'org.apache.httpcomponents:httpclient'
        testImplementation 'org.mock-server:mockserver-netty:5.6.1'
}

appengine {
  deploy {
    projectId = 'personal-website-std-j11'
    version = 'GCLOUD_CONFIG'
    promote = true
    stopPreviousVersion = true
  }
  tools {
    cloudSdkHome = '/home/nickl/my/usr/lib/google-cloud-sdk'
  }
}

My Gradle version details:

$ ./gradlew --version

------------------------------------------------------------
Gradle 6.5.1
------------------------------------------------------------

Build time:   2020-06-30 06:32:47 UTC
Revision:     66bc713f7169626a7f0134bf452abde51550ea0a

Kotlin:       1.3.72
Groovy:       2.5.11
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          11.0.8 (Azul Systems, Inc. 11.0.8+10-LTS)
OS:           Linux 5.4.0-42-generic amd64

My app.yaml:

$ cat ./src/main/appengine/app.yaml
runtime: java11
entrypoint: java -Dserver.port=${PORT:-8080} -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling -jar personal-website-undefined.jar
instance_class: F2
commented

Sorry I thought I had replied to this. I wasn't really able to recreate it. If you could provide a minimal project that does it will help us debug. The jar property has been working for a while, so I don't know what's going on here

Hello, I have the same issue. It seems to be caused by "Task Configuration Avoidance" introduced in Gradle 5.1.

It happens when you don't trigger any code in your build.gradle file that configures the Gradle Jar task. The reason is that since Gradle 5.1 when you apply the java plugin to your project the Jar task is by default only registered but not configured/created if you don't explicitly do so. The way AppEngineAppYamlPlugin gets the Jar task does not trigger the creation if the task has only been registered and thus gets a null reference causing NPE later.

A quick fix for users of the App Engine Gradle plugin is to do something in the build.gradle file that creates the Jar task. Adding following line should be sufficient:
jar {}

Possible solution to fix the NPE in the plugin code is to get the Jar (or War) task in a way that makes sure it is created:

diff --git a/src/main/java/com/google/cloud/tools/gradle/appengine/appyaml/AppEngineAppYamlPlugin.java b/src/main/java/com/google/cloud/tools/gradle/appengine/appyaml/AppEngineAppYamlPlugin.java
index 28ca50d..d7bbf7f 100644
--- a/src/main/java/com/google/cloud/tools/gradle/appengine/appyaml/AppEngineAppYamlPlugin.java
+++ b/src/main/java/com/google/cloud/tools/gradle/appengine/appyaml/AppEngineAppYamlPlugin.java
@@ -89,10 +89,10 @@ public class AppEngineAppYamlPlugin implements Plugin<Project> {
           // we can only set the default location of "archive" after project evaluation (callback)
           if (stageExtension.getArtifact() == null) {
             if (project.getPlugins().hasPlugin(WarPlugin.class)) {
-              War war = (War) project.getProperties().get("war");
+              War war = (War) project.getTasks().getByName("war");
               stageExtension.setArtifact(war.getArchivePath());
             } else if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
-              Jar jar = (Jar) project.getProperties().get("jar");
+              Jar jar = (Jar) project.getTasks().getByName("jar");
               stageExtension.setArtifact(jar.getArchivePath());
             } else {
               throw new GradleException("Could not find JAR or WAR configuration");

There might be still some way to avoid Jar task creation for performance reasons if it's not needed for current build but it may require bigger changes in the plugin code.

@pmatousek Thanks for your input! This will be useful for others facing the same issue and/or if we want to fix it in the plugin at some point.

For folks hitting this at the exact same location, a workaround is to simply execute the jar (or war) task with our plugin tasks. For example, ./gradlew jar appengineDeploy. The cause is likely what @pmatousek said: the jar task may not be created in recent Gradle versions. Note that you do need a JAR (or WAR) to deploy, so running jar is an implicit requirement.