Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GCP HTTP Example fails to deploy - ClassNotFound exception - JarLauncher #1085

Open
ggranum opened this issue Oct 24, 2023 · 8 comments
Open

Comments

@ggranum
Copy link

ggranum commented Oct 24, 2023

Describe the bug
Building and deploying the Main branch version of spring-cloud-function-samples/function-sample-gcp-http fails with 'java.lang.ClassNotFoundException: org.springframework.boot.loader.JarLauncher'

The 4.0.x branch still packages and deploys as expected.

Steps
Clone project && cd project
./mvnw install
cd spring-cloud-function-samples/function-sample-gcp-http
mvn package
gcloud functions deploy function-sample-gcp-http
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher
--runtime java17
--trigger-http
--source target/deploy
--memory 512MB

deploy fails with above exception.

What works
cd projectRoot
git checkout 4.0.x
mvn clean
cd spring-cloud-function-samples/function-sample-gcp-http
mvn package
gcloud functions deploy function-sample-gcp-http
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher
--runtime java17
--trigger-http
--source target/deploy
--memory 512MB

Further Info
I tried JDK20, 21 and 17. All Temurin.

@SND33
Copy link

SND33 commented Dec 12, 2023

We're suffering from the same issue. After upgrading Spring Boot to 3.2.0, we started getting this error on startup when deployed on GCP. Upon inspecting the JAR built by the Spring Boot Maven plugin, we can see that indeed the JarLauncher class it not in that place. This causes the error because GcfJarLauncher extends JarLauncher.

The Spring Boot 3.2.0 release notes explain that the Spring Boot loader tools have moved to the launch package, which explains the error. They also provide a workaround to use the classic loader tools by adding following configuration to the Spring Boot Maven plugin:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <executions>
        <execution>
          <goals>
            <goal>repackage</goal>
          </goals>
          <configuration>
            <loaderImplementation>CLASSIC</loaderImplementation>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

When doing that, the classic loader tools are packaged by the plugin, which should in theory resolve the issue. However, we found out that's in fact the spring-cloud-function-adapter-gcp dependency, which must be added as a dependency for the plugin, that overwrites this behaviour and ends up not including the classic loader tools (we confirmed that without the adapter, and with the above change, the built JAR does include the classic loader tools).

Is anyone aware of a workaround? If not, we're forced to downgrade Spring Boot, as well as Spring Cloud.

UPDATE: downgrading to Spring Boot 3.1.6 resolved the issue as expected

@ecky-l
Copy link

ecky-l commented Feb 22, 2024

Same here. Also looking for a workaround or a solution.

@cfranzen
Copy link

Looking for a solution for Gradel. There seems to be no solution at the moment.

@Masahito-I
Copy link

Masahito-I commented Mar 23, 2024

I'm also facing this issue. Is there any solution?

P.S. for now, I decided to downgrade the SpringBoot version from 3.2.4 to 3.1.10.
The deployment was successful after that.
If you'd like to implement your function ASAP, please consider to downgrade it.

@PMG-VascoSaavedra
Copy link

PMG-VascoSaavedra commented Mar 29, 2024

I was able to sort this out, with the help of a friend.

Basically, i would have to change the signature to receive an Object, and then cast to a BufferedReader.
Afterwards, i would have to read from it line by line, parse the lines and create a Key/Value Map.

I tested this and it worked.

@SpringBootApplication
public class CloudFunctionMain {

    private static final Logger log = LoggerFactory.getLogger(CloudFunctionMain.class);

    public static void main(String[] args) {
		SpringApplication.run(CloudFunctionMain.class, args);
    }

    @Bean
    public Function<Object, ResponseEntity<Object>> function() {
    return this::handleNotify;
    }

    private ResponseEntity<Object> handleNotify(final Object values) {

		BufferedReader request = ((BufferedReader) values);

                //Read BufferedReader, parse the lines and add values to a Map.

		
		return new ResponseEntity<>(null, new HttpHeaders(), HttpStatus.OK);
    }
}

In the end, i opted to use Quarkus.

@MawsFr
Copy link

MawsFr commented Apr 23, 2024

Here is a working pom.xml based on what @Masahito-I said. It uses GCP spring cloud function + Webflux + gcp secret manager.

I think this is linked to the fact that JarLauncher has been moved but the current version of spring-cloud-function seems to point to the old JarLauncher folder (Source).

Hope it will be fixed soon ^^

<?xml version="1.0" encoding="UTF-8"?>
<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.examples</groupId>
    <artifactId>example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>example</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.10</version> <!-- DO NOT UPGRADE see : https://github.com/spring-cloud/spring-cloud-function/issues/1085 -->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>21</java.version>
        <spring-cloud-function.version>4.1.1</spring-cloud-function.version>
        <mockito-core.version>5.11.0</mockito-core.version>
        <logstash-logback-encoder.version>7.4</logstash-logback-encoder.version>
        <google-cloud-storage.version>2.36.1</google-cloud-storage.version>
        <java-function-invoker.version>1.3.1</java-function-invoker.version>
        <spring-cloud-gcp-starter-secretmanager.version>5.1.2</spring-cloud-gcp-starter-secretmanager.version>
        <spring-cloud-gcp-dependencies.version>4.8.4</spring-cloud-gcp-dependencies.version>

        <!-- JUnit Properties -->
        <junit-bom.version>5.10.2</junit-bom.version>

        <!-- JaCoCo Properties -->
        <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
        <maven-failsafe-plugin.version>3.2.5</maven-failsafe-plugin.version>
        <junit-jupiter-engine.version>5.10.2</junit-jupiter-engine.version>
        <jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-function-webflux</artifactId>
            <version>${spring-cloud-function.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-adapter-gcp</artifactId>
            <version>${spring-cloud-function.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- DO NOT DELETE THIS https://stackoverflow.com/a/78164824 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
        </dependency>

        <!-- logs -->
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>${logstash-logback-encoder.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

        <!-- To use gcp bucket -->
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-storage</artifactId>
            <version>${google-cloud-storage.version}</version>
        </dependency>

        <!-- Add Secret Manager Starter -->
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>spring-cloud-gcp-starter-secretmanager</artifactId>
            <version>${spring-cloud-gcp-starter-secretmanager.version}</version>
        </dependency>

        <!-- tools -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>${mockito-core.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.cloud.functions.invoker</groupId>
            <artifactId>java-function-invoker</artifactId>
            <version>${java-function-invoker.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>${mockito-core.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <outputDirectory>target/deploy</outputDirectory>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.cloud</groupId>
                        <artifactId>spring-cloud-function-adapter-gcp</artifactId>
                        <version>${spring-cloud-function.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

            <plugin>
                <groupId>com.google.cloud.functions</groupId>
                <artifactId>function-maven-plugin</artifactId>
                <version>0.9.1</version>
                <configuration>
                    <functionTarget>org.springframework.cloud.function.adapter.gcp.GcfJarLauncher</functionTarget>
                    <port>8080</port>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>org.junit.jupiter</groupId>
                        <artifactId>junit-jupiter-engine</artifactId>
                        <version>${junit-jupiter-engine.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${maven-failsafe-plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                        <configuration>
                            <includes>
                                <include>**/*IT.java</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
                <configuration>
                    <!-- failsafe coverage per test reports can be shown in Sonar
                    only if they are put in the same dir as surefire reports -->
                    <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco-maven-plugin.version}</version>
                <executions>
                    <execution>
                        <id>jacoco-initialize</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>prepare-agent-it</id>
                        <goals>
                            <goal>prepare-agent-integration</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>jacoco-site</id>
                        <phase>package</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/jacoco.exec</dataFile>
                        </configuration>
                    </execution>
                    <execution>
                        <id>report-it</id>
                        <goals>
                            <goal>report-integration</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/jacoco-it.exec</dataFile>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <!-- DO NOT CHANGE : https://stackoverflow.com/a/78207517 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>${junit-bom.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
            <dependency>
                <groupId>com.google.cloud</groupId>
                <artifactId>spring-cloud-gcp-dependencies</artifactId>
                <version>${spring-cloud-gcp-dependencies.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <profiles>
        <profile>
            <id>macos-aarch64</id>
            <activation>
                <os>
                    <family>mac</family>
                    <arch>aarch64</arch>
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>io.netty</groupId>
                    <artifactId>netty-resolver-dns-native-macos</artifactId>
                    <version>${netty.version}</version>
                    <classifier>osx-aarch_64</classifier>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

</project>

@petrosyanar
Copy link

petrosyanar commented May 28, 2024

The same issue here. The Spring Boot 3.3.x is blocked now in our cloud functions

@Mowee
Copy link

Mowee commented Jun 5, 2024

We have the same problem with Gradle. We are currently still using Spring Boot 3.1 which unfortunately lost support last month.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants