diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 6a9935ffbd505b..2e5b82b87bc54a 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -194,7 +194,7 @@
3.17.3
${protobuf-java.version}
4.6.1
- 1.0.1
+ 1.0.4
1.21
2.8.6
0.46
diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyBackgroundFunction.java b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyBackgroundFunction.java
index 8e612af8c7704c..92743edc5564e1 100644
--- a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyBackgroundFunction.java
+++ b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyBackgroundFunction.java
@@ -24,7 +24,7 @@ public class FunqyBackgroundFunction implements RawBackgroundFunction {
Thread.currentThread().setContextClassLoader(FunqyBackgroundFunction.class.getClassLoader());
Class> appClass = Class.forName("io.quarkus.runner.ApplicationImpl");
String[] args = {};
- Application app = (Application) appClass.newInstance();
+ Application app = (Application) appClass.getConstructor().newInstance();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudEventsFunction.java b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudEventsFunction.java
new file mode 100644
index 00000000000000..eff2b350299242
--- /dev/null
+++ b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudEventsFunction.java
@@ -0,0 +1,57 @@
+package io.quarkus.funqy.gcp.functions;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import com.google.cloud.functions.CloudEventsFunction;
+
+import io.cloudevents.CloudEvent;
+import io.quarkus.runtime.Application;
+
+public class FunqyCloudEventsFunction implements CloudEventsFunction {
+ protected static final String deploymentStatus;
+ protected static boolean started = false;
+
+ static {
+ StringWriter error = new StringWriter();
+ PrintWriter errorWriter = new PrintWriter(error, true);
+ if (Application.currentApplication() == null) { // were we already bootstrapped? Needed for mock unit testing.
+ // For GCP functions, we need to set the TCCL to the QuarkusHttpFunction classloader then restore it.
+ // Without this, we have a lot of classloading issues (ClassNotFoundException on existing classes)
+ // during static init.
+ ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(FunqyCloudEventsFunction.class.getClassLoader());
+ Class> appClass = Class.forName("io.quarkus.runner.ApplicationImpl");
+ String[] args = {};
+ Application app = (Application) appClass.getConstructor().newInstance();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ app.stop();
+ }
+ });
+ app.start(args);
+ errorWriter.println("Quarkus bootstrapped successfully.");
+ started = true;
+ } catch (Exception ex) {
+ errorWriter.println("Quarkus bootstrap failed.");
+ ex.printStackTrace(errorWriter);
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentCl);
+ }
+ } else {
+ errorWriter.println("Quarkus bootstrapped successfully.");
+ started = true;
+ }
+ deploymentStatus = error.toString();
+ }
+
+ @Override
+ public void accept(CloudEvent cloudEvent) throws Exception {
+ if (!started) {
+ throw new RuntimeException(deploymentStatus);
+ }
+ FunqyCloudFunctionsBindingRecorder.handle(cloudEvent);
+ }
+}
diff --git a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java
index fa4b57b26e3372..e16a6fdd6b5ce2 100644
--- a/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java
+++ b/extensions/funqy/funqy-google-cloud-functions/runtime/src/main/java/io/quarkus/funqy/gcp/functions/FunqyCloudFunctionsBindingRecorder.java
@@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.cloud.functions.Context;
+import io.cloudevents.CloudEvent;
import io.quarkus.arc.ManagedContext;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.funqy.runtime.FunctionConstructor;
@@ -96,6 +97,20 @@ public static void handle(String event, Context context) {
}
}
+ /**
+ * Handle CloudEventsFunction
+ *
+ * @param cloudEvent
+ */
+ public static void handle(CloudEvent cloudEvent) {
+ FunqyServerResponse response = dispatch(cloudEvent);
+
+ Object value = response.getOutput().await().indefinitely();
+ if (value != null) {
+ throw new RuntimeException("A background function cannot return a value");
+ }
+ }
+
private static FunqyServerResponse dispatch(Object input) {
ManagedContext requestContext = beanContainer.requestContext();
requestContext.activate();
diff --git a/extensions/google-cloud-functions/deployment/src/main/java/io/quarkus/gcp/functions/deployment/GoogleCloudFunctionsProcessor.java b/extensions/google-cloud-functions/deployment/src/main/java/io/quarkus/gcp/functions/deployment/GoogleCloudFunctionsProcessor.java
index c9e032b62e44f2..de99ed97b30cc1 100755
--- a/extensions/google-cloud-functions/deployment/src/main/java/io/quarkus/gcp/functions/deployment/GoogleCloudFunctionsProcessor.java
+++ b/extensions/google-cloud-functions/deployment/src/main/java/io/quarkus/gcp/functions/deployment/GoogleCloudFunctionsProcessor.java
@@ -14,6 +14,7 @@
import org.jboss.jandex.IndexView;
import com.google.cloud.functions.BackgroundFunction;
+import com.google.cloud.functions.CloudEventsFunction;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.RawBackgroundFunction;
@@ -36,6 +37,7 @@ public class GoogleCloudFunctionsProcessor {
public static final DotName DOTNAME_HTTP_FUNCTION = DotName.createSimple(HttpFunction.class.getName());
public static final DotName DOTNAME_BACKGROUND_FUNCTION = DotName.createSimple(BackgroundFunction.class.getName());
public static final DotName DOTNAME_RAW_BACKGROUND_FUNCTION = DotName.createSimple(RawBackgroundFunction.class.getName());
+ public static final DotName DOTNAME_CLOUD_EVENT_FUNCTION = DotName.createSimple(CloudEventsFunction.class.getName());
@BuildStep
public FeatureBuildItem feature() {
@@ -56,6 +58,7 @@ public List discoverFunctionClass(CombinedIndexBuildItem
Collection httpFunctions = index.getAllKnownImplementors(DOTNAME_HTTP_FUNCTION);
Collection backgroundFunctions = index.getAllKnownImplementors(DOTNAME_BACKGROUND_FUNCTION);
Collection rawBackgroundFunctions = index.getAllKnownImplementors(DOTNAME_RAW_BACKGROUND_FUNCTION);
+ Collection cloudEventFunctions = index.getAllKnownImplementors(DOTNAME_CLOUD_EVENT_FUNCTION);
List cloudFunctions = new ArrayList<>();
cloudFunctions.addAll(
@@ -65,6 +68,8 @@ public List discoverFunctionClass(CombinedIndexBuildItem
cloudFunctions.addAll(
registerFunctions(unremovableBeans, rawBackgroundFunctions,
GoogleCloudFunctionInfo.FunctionType.RAW_BACKGROUND));
+ cloudFunctions.addAll(
+ registerFunctions(unremovableBeans, cloudEventFunctions, GoogleCloudFunctionInfo.FunctionType.CLOUD_EVENT));
if (cloudFunctions.isEmpty()) {
throw new BuildException("No Google Cloud Function found on the classpath", Collections.emptyList());
diff --git a/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionInfo.java b/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionInfo.java
index 46c3aa9a0de9d9..d9f8dc5a4ef44c 100644
--- a/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionInfo.java
+++ b/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionInfo.java
@@ -29,9 +29,10 @@ public void setFunctionType(FunctionType functionType) {
this.functionType = functionType;
}
- public static enum FunctionType {
+ public enum FunctionType {
HTTP,
BACKGROUND,
- RAW_BACKGROUND;
+ RAW_BACKGROUND,
+ CLOUD_EVENT
}
}
diff --git a/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionRecorder.java b/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionRecorder.java
index d7acc2b2274146..8f4cb73a0d2466 100644
--- a/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionRecorder.java
+++ b/extensions/google-cloud-functions/runtime/src/main/java/io/quarkus/gcp/functions/GoogleCloudFunctionRecorder.java
@@ -37,5 +37,6 @@ public void selectDelegate(GoogleCloudFunctionsConfig config, List appClass = Class.forName("io.quarkus.runner.ApplicationImpl");
+ String[] args = {};
+ Application app = (Application) appClass.getConstructor().newInstance();
+ app.start(args);
+ errorWriter.println("Quarkus bootstrapped successfully.");
+ started = true;
+ } catch (Exception ex) {
+ errorWriter.println("Quarkus bootstrap failed.");
+ ex.printStackTrace(errorWriter);
+ } finally {
+ Thread.currentThread().setContextClassLoader(currentCl);
+ }
+ } else {
+ errorWriter.println("Quarkus bootstrapped successfully.");
+ started = true;
+ }
+ deploymentStatus = error.toString();
+ }
+
+ static void setDelegate(String selectedDelegate) {
+ if (selectedDelegate != null) {
+ try {
+ Class> clazz = Class.forName(selectedDelegate, false, Thread.currentThread().getContextClassLoader());
+ delegate = (CloudEventsFunction) Arc.container().instance(clazz).get();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void accept(CloudEvent cloudEvent) throws Exception {
+ if (!started) {
+ throw new IOException(deploymentStatus);
+ }
+
+ // TODO maybe we can check this at static init
+ if (delegate == null) {
+ throw new IOException("We didn't found any CloudEventsFunction to run " +
+ "(or there is multiple one and none selected inside your application.properties)");
+ }
+
+ delegate.accept(cloudEvent);
+ }
+}
diff --git a/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java b/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java
index 9ec3c7ee68dcf0..c7e1bb98ad909c 100644
--- a/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java
+++ b/integration-tests/funqy-google-cloud-functions/src/main/java/io/quarkus/funqy/gcp/functions/test/GreetingFunctions.java
@@ -2,6 +2,7 @@
import javax.inject.Inject;
+import io.cloudevents.CloudEvent;
import io.quarkus.funqy.Funq;
import io.quarkus.funqy.gcp.functions.event.PubsubMessage;
import io.quarkus.funqy.gcp.functions.event.StorageEvent;
@@ -23,4 +24,13 @@ public void helloGCSWorld(StorageEvent storageEvent) {
System.out.println(storageEvent.name + " - " + message);
}
+ @Funq
+ public void helloCloudEvent(CloudEvent cloudEvent) {
+ System.out.println("Receive event Id: " + cloudEvent.getId());
+ System.out.println("Receive event Subject: " + cloudEvent.getSubject());
+ System.out.println("Receive event Type: " + cloudEvent.getType());
+ System.out.println("Receive event Data: " + new String(cloudEvent.getData().toBytes()));
+ System.out.println("Be polite, say " + service.hello("world"));
+ }
+
}
diff --git a/integration-tests/funqy-google-cloud-functions/src/main/resources/application.properties b/integration-tests/funqy-google-cloud-functions/src/main/resources/application.properties
index d35c85ba890498..59d042700da56c 100644
--- a/integration-tests/funqy-google-cloud-functions/src/main/resources/application.properties
+++ b/integration-tests/funqy-google-cloud-functions/src/main/resources/application.properties
@@ -1,2 +1,3 @@
-quarkus.funqy.export=helloGCSWorld
+#quarkus.funqy.export=helloGCSWorld
#quarkus.funqy.export=helloPubSubWorld
+quarkus.funqy.export=helloCloudEvent
diff --git a/integration-tests/google-cloud-functions/src/main/java/io/quarkus/gcp/function/test/CloudEventStorageTest.java b/integration-tests/google-cloud-functions/src/main/java/io/quarkus/gcp/function/test/CloudEventStorageTest.java
new file mode 100644
index 00000000000000..9d2c358600ffb4
--- /dev/null
+++ b/integration-tests/google-cloud-functions/src/main/java/io/quarkus/gcp/function/test/CloudEventStorageTest.java
@@ -0,0 +1,26 @@
+package io.quarkus.gcp.function.test;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import com.google.cloud.functions.CloudEventsFunction;
+
+import io.cloudevents.CloudEvent;
+import io.quarkus.gcp.function.test.service.GreetingService;
+
+@Named("cloudEventTest")
+@ApplicationScoped
+public class CloudEventStorageTest implements CloudEventsFunction {
+ @Inject
+ GreetingService greetingService;
+
+ @Override
+ public void accept(CloudEvent cloudEvent) throws Exception {
+ System.out.println("Receive event Id: " + cloudEvent.getId());
+ System.out.println("Receive event Subject: " + cloudEvent.getSubject());
+ System.out.println("Receive event Type: " + cloudEvent.getType());
+ System.out.println("Receive event Data: " + new String(cloudEvent.getData().toBytes()));
+ System.out.println("Be polite, say " + greetingService.hello());
+ }
+}
diff --git a/integration-tests/google-cloud-functions/src/main/resources/application.properties b/integration-tests/google-cloud-functions/src/main/resources/application.properties
index c5c81be1fce44f..836825d7531740 100644
--- a/integration-tests/google-cloud-functions/src/main/resources/application.properties
+++ b/integration-tests/google-cloud-functions/src/main/resources/application.properties
@@ -1,3 +1,4 @@
-quarkus.google-cloud-functions.function=httpTest
+#quarkus.google-cloud-functions.function=httpTest
+quarkus.google-cloud-functions.function=cloudEventTest
#quarkus.google-cloud-functions.function=rawPubSubTest
#quarkus.google-cloud-functions.function=storageTest
\ No newline at end of file