diff --git a/docs/src/main/asciidoc/adapters/gcp-intro.adoc b/docs/src/main/asciidoc/adapters/gcp-intro.adoc
index e44de79d7..c656366ad 100644
--- a/docs/src/main/asciidoc/adapters/gcp-intro.adoc
+++ b/docs/src/main/asciidoc/adapters/gcp-intro.adoc
@@ -36,7 +36,7 @@ Start by adding the Maven plugin provided as part of the Google Functions Framew
function-maven-plugin
0.9.1
- org.springframework.cloud.function.adapter.gcp.FunctionInvoker
+ org.springframework.cloud.function.adapter.gcp.GcfJarLauncher
8080
@@ -66,67 +66,44 @@ curl http://localhost:8080/ -d "hello"
As of March 2020, Google Cloud Functions for Java is in Alpha.
You can get on the https://docs.google.com/forms/d/e/1FAIpQLScC98jGi7CfG0n3UYlj7Xad8XScvZC8-BBOg7Pk3uSZx_2cdQ/viewform[whitelist] to try it out.
-To deploy to Google Cloud Function, you need to produce a fat jar using the Shade plugin, rather than the Spring Boot plugin.
+In order to use the adapter, first add the dependency to your pom.xml:
+
+[source, xml]
+----
+
+ org.springframework.cloud
+ spring-cloud-function-adapter-gcp
+
+----
+
+Then, add the `spring-boot-maven-plugin` with `spring-cloud-function-adapter-gcp` as a dependency.
+The extra dependency is used for `spring-boot-maven-plugin` to package your function in the correct JAR format for deployment on Google Cloud Functions.
-First, if you already have the Spring Boot plugin in your `pom.xml`, *remove* it:
[source, xml]
----
-
-
-----
-
-Then, *add* the Shade Plugin configuration to generate a fat jar when you run the `mvn package` command.
-
-[source, xml]
-----
-
- org.apache.maven.plugins
- maven-shade-plugin
-
-
- package
-
- shade
-
-
- true
- target/deploy
- gcp
-
-
- META-INF/spring.handlers
-
-
- META-INF/spring.factories
-
-
- META-INF/spring.schemas
-
-
-
- com.example.CloudFunctionMain
-
-
-
-
-
+
+ target/deploy
+
+
+
+ org.springframework.cloud
+ spring-cloud-function-adapter-gcp
+
+
----
-IMPORTANT: If both Spring Boot plugin and Shade plugin are present, Shade plugin may be shading a Spring Boot produced JAR, resulting in a Fat JAR that's unusable in Google Cloud Function. Don't forget to remove the Spring Boot plugin!
-
Package the application.
----
mvn package
----
-You should see the fat jar in `target/deploy` directory.
+You should see the resulting JAR in `target/deploy` directory.
+This JAR is correctly formatted for deployment to Google Cloud Functions.
Make sure that you have the https://cloud.google.com/sdk/install[Cloud SDK CLI] installed.
@@ -134,7 +111,7 @@ From the project base directory run the following command to deploy.
----
gcloud alpha functions deploy function-sample-gcp \
---entry-point org.springframework.cloud.function.adapter.gcp.FunctionInvoker \
+--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-http \
--source target/deploy \
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
index 91a204c04..a35b1d77d 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
@@ -26,7 +26,6 @@
com.google.cloud.functions
functions-framework-api
${google.cloud.functions.api.version}
- provided
com.google.code.gson
@@ -36,6 +35,17 @@
org.springframework.cloud
spring-cloud-function-context
+
+
+
+ org.springframework.boot
+ spring-boot-loader-tools
+
+
+ org.springframework.boot
+ spring-boot-loader
+
+
org.springframework.boot
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java
new file mode 100644
index 000000000..9e7a1d393
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/GcfJarLauncher.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.function.adapter.gcp;
+
+import com.google.cloud.functions.HttpFunction;
+import com.google.cloud.functions.HttpRequest;
+import com.google.cloud.functions.HttpResponse;
+
+import org.springframework.boot.loader.JarLauncher;
+import org.springframework.boot.loader.jar.JarFile;
+
+/**
+ * The launcher class written at the top-level of the output JAR to be deployed to
+ * Google Cloud Functions. This is the entry point to the function when run from JAR.
+ *
+ * @author Ray Tsang
+ * @author Daniel Zou
+ */
+public class GcfJarLauncher extends JarLauncher implements HttpFunction {
+
+ private final ClassLoader loader;
+
+ private final HttpFunction delegate;
+
+ public GcfJarLauncher() throws Exception {
+ JarFile.registerUrlProtocolHandler();
+
+ this.loader = createClassLoader(getClassPathArchivesIterator());
+
+ Class> clazz = this.loader
+ .loadClass("org.springframework.cloud.function.adapter.gcp.FunctionInvoker");
+ this.delegate = (HttpFunction) clazz.getConstructor().newInstance();
+ }
+ @Override
+ public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception {
+ Thread.currentThread().setContextClassLoader(this.loader);
+ delegate.service(httpRequest, httpResponse);
+ }
+}
+
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java
new file mode 100644
index 000000000..874e0c66a
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayout.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.function.adapter.gcp.layout;
+
+import java.io.IOException;
+
+import org.springframework.boot.loader.tools.CustomLoaderLayout;
+import org.springframework.boot.loader.tools.Layouts;
+import org.springframework.boot.loader.tools.LoaderClassesWriter;
+import org.springframework.cloud.function.adapter.gcp.GcfJarLauncher;
+
+/**
+ * A custom JAR Layout that writes GCF adapter classes to the top-level of the output JAR
+ * for deploying to GCF.
+ *
+ * @author Ray Tsang
+ * @author Daniel Zou
+ */
+public class GcfJarLayout extends Layouts.Jar implements CustomLoaderLayout {
+
+ private static final String LAUNCHER_NAME = GcfJarLauncher.class.getCanonicalName();
+
+ @Override
+ public String getLauncherClassName() {
+ return LAUNCHER_NAME;
+ }
+
+ @Override
+ public boolean isExecutable() {
+ return false;
+ }
+
+ @Override
+ public void writeLoadedClasses(LoaderClassesWriter writer) throws IOException {
+ writer.writeLoaderClasses();
+
+ String jarName = LAUNCHER_NAME.replaceAll("\\.", "/") + ".class";
+ writer.writeEntry(
+ jarName, GcfJarLauncher.class.getResourceAsStream("/" + jarName));
+ }
+}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java
new file mode 100644
index 000000000..87eacef00
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/layout/GcfJarLayoutFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018-2020 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.function.adapter.gcp.layout;
+
+import java.io.File;
+
+import org.springframework.boot.loader.tools.Layout;
+import org.springframework.boot.loader.tools.LayoutFactory;
+
+/**
+ * Factory boilerplate class that constructs {@link GcfJarLayout}.
+ *
+ * @author Ray Tsang
+ * @author Daniel Zou
+ */
+public class GcfJarLayoutFactory implements LayoutFactory {
+
+ @Override
+ public Layout getLayout(File source) {
+ return new GcfJarLayout();
+ }
+}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..76392ffda
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.loader.tools.LayoutFactory=\
+org.springframework.cloud.function.adapter.gcp.layout.GcfJarLayoutFactory
diff --git a/spring-cloud-function-samples/function-sample-gcp/README.adoc b/spring-cloud-function-samples/function-sample-gcp/README.adoc
index f12b939a4..000f0aa3c 100644
--- a/spring-cloud-function-samples/function-sample-gcp/README.adoc
+++ b/spring-cloud-function-samples/function-sample-gcp/README.adoc
@@ -35,7 +35,7 @@ Run the following command from the project root to deploy.
----
gcloud alpha functions deploy function-sample-gcp \
---entry-point org.springframework.cloud.function.adapter.gcp.FunctionInvoker \
+--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-http \
--source target/deploy \
diff --git a/spring-cloud-function-samples/function-sample-gcp/pom.xml b/spring-cloud-function-samples/function-sample-gcp/pom.xml
index 30d53f22a..02463d804 100644
--- a/spring-cloud-function-samples/function-sample-gcp/pom.xml
+++ b/spring-cloud-function-samples/function-sample-gcp/pom.xml
@@ -2,21 +2,28 @@
+
4.0.0
+ io.spring.sample
function-sample-gcp
+ 2.0.0.RELEASE
+ jar
+
+ function-sample-gcp
- spring-cloud-function-samples
- org.springframework.cloud
- 3.1.0.BUILD-SNAPSHOT
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.0.BUILD-SNAPSHOT
+
org.springframework.cloud
spring-cloud-function-adapter-gcp
- ${project.version}
+ 3.1.0.BUILD-SNAPSHOT
@@ -45,49 +52,30 @@
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ target/deploy
+
+
+
+ org.springframework.cloud
+ spring-cloud-function-adapter-gcp
+ 3.1.0.BUILD-SNAPSHOT
+
+
+
+
com.google.cloud.functions
function-maven-plugin
0.9.1
- org.springframework.cloud.function.adapter.gcp.FunctionInvoker
+ org.springframework.cloud.function.adapter.gcp.GcfJarLauncher
8080
-
-
- org.apache.maven.plugins
- maven-shade-plugin
-
-
- package
-
- shade
-
-
- true
- target/deploy
- gcp
-
-
- META-INF/spring.handlers
-
-
- META-INF/spring.factories
-
-
- META-INF/spring.schemas
-
-
-
- com.example.CloudFunctionMain
-
-
-
-
-
-
-