Lenguajes de JVM

Puedes escribir tu función con diferentes lenguajes de JVM (como Kotlin, Groovy o Scala) siempre que se cumplan las siguientes reglas:

  • La función es una clase pública que implementa una de las interfaces de la función (HttpFunction, BackgroundFunction o RawBackgroundFunction) y tiene un constructor público sin argumentos.

  • Si realizas la implementación desde la fuente:

    • Se puede compilar desde Maven.
    • El archivo de compilación contiene todos los complementos necesarios para producir clases compiladas.
  • Si realizas la implementación desde un archivo JAR compilado previamente, haz lo siguiente:

    • Puedes usar cualquier herramienta de compilación para producir este archivo JAR.
    • El archivo JAR ya compilado debe ser un fat JAR con todas sus clases de dependencia, o su manifiesto debe contener una entrada Class-Path con las ubicaciones relativas de los archivos JAR que contienen esas clases de dependencia.

Ejemplos de funciones de HTTP

Debes usar funciones de HTTP cuando quieres invocar tu función a través de una solicitud HTTP(S). Los siguientes ejemplos generan el mensaje "Hello World!"

Kotlin

import com.google.cloud.functions.HttpFunction
import com.google.cloud.functions.HttpRequest
import com.google.cloud.functions.HttpResponse
import java.io.IOException
import java.util.logging.Logger

class HelloWorld : HttpFunction {
    // Simple function to return "Hello World"
    @Throws(IOException::class)
    override fun service(request: HttpRequest, response: HttpResponse) {
        response.writer.write("Hello World!")
    }
}

Groovy

import com.google.cloud.functions.HttpFunction
import com.google.cloud.functions.HttpRequest
import com.google.cloud.functions.HttpResponse

class GroovyHelloWorld implements HttpFunction {
    @Override
    void service(HttpRequest request, HttpResponse response) {
        response.writer.write("Hello World!")
    }
}

Scala

class ScalaHelloWorld extends HttpFunction {
  override def service(httpRequest: HttpRequest, httpResponse: HttpResponse): Unit = {
    httpResponse.getWriter.write("Hello World!")
  }
}

Archivo pom.xml para ejemplos de HTTP

Estos son los archivos pom.xml de los ejemplos anteriores:

Kotlin

<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.functions</groupId>
  <artifactId>functions-kotlin-hello-world</artifactId>

  <properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <kotlin.version>1.9.22</kotlin.version>
  </properties>

  <dependencies>
    <!-- Required for Function primitives -->
    <dependency>
      <groupId>com.google.cloud.functions</groupId>
      <artifactId>functions-framework-api</artifactId>
      <version>1.1.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-stdlib-jdk8</artifactId>
      <version>${kotlin.version}</version>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-test</artifactId>
      <version>${kotlin.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <!--
          Google Cloud Functions Framework Maven plugin

          This plugin allows you to run Cloud Functions Java code
          locally. Use the following terminal command to run a
          given function locally:

          mvn function:run -Drun.functionTarget=your.package.yourFunction
        -->
        <groupId>com.google.cloud.functions</groupId>
        <artifactId>function-maven-plugin</artifactId>
        <version>0.11.0</version>
        <configuration>
          <functionTarget>functions.HelloWorld</functionTarget>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-maven-plugin</artifactId>
        <version>${kotlin.version}</version>
        <executions>
          <execution>
            <id>compile</id>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
            <configuration>
              <sourceDirs>
                <source>src/main/kotlin</source>
              </sourceDirs>
            </configuration>
          </execution>
          <execution>
            <id>test-compile</id>
            <phase>test-compile</phase>
            <goals>
              <goal>test-compile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <jvmTarget>1.8</jvmTarget>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Groovy

<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.functions</groupId>
  <artifactId>functions-groovy-hello-world</artifactId>

  <properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <groovy.version>3.0.20</groovy.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <artifactId>libraries-bom</artifactId>
        <groupId>com.google.cloud</groupId>
        <scope>import</scope>
        <type>pom</type>
        <version>26.32.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!-- Required for Function primitives -->
    <dependency>
      <groupId>com.google.cloud.functions</groupId>
      <artifactId>functions-framework-api</artifactId>
      <version>1.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- Required for groovy samples -->
    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy-all</artifactId>
      <version>${groovy.version}</version>
      <type>pom</type>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <!--
          Google Cloud Functions Framework Maven plugin

          This plugin allows you to run Cloud Functions Java code
          locally. Use the following terminal command to run a
          given function locally:

          mvn function:run -Drun.functionTarget=your.package.yourFunction
        -->
        <groupId>com.google.cloud.functions</groupId>
        <artifactId>function-maven-plugin</artifactId>
        <version>0.11.0</version>
        <configuration>
          <functionTarget>functions.GroovyHelloWorld</functionTarget>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.gmavenplus</groupId>
        <artifactId>gmavenplus-plugin</artifactId>
        <version>3.0.2</version>
        <executions>
          <execution>
            <id>groovy-compile</id>
            <phase>process-resources</phase>
            <goals>
              <goal>addSources</goal>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <!-- any version of Groovy \>= 1.5.0 should work here -->
            <version>${groovy.version}</version>
            <scope>runtime</scope>
            <type>pom</type>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>

Scala

<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.functions</groupId>
  <artifactId>functions-scala-hello-world</artifactId>

  <properties>
    <maven.compiler.target>11</maven.compiler.target>
    <maven.compiler.source>11</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <artifactId>libraries-bom</artifactId>
        <groupId>com.google.cloud</groupId>
        <scope>import</scope>
        <type>pom</type>
        <version>26.32.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!-- Required for scala samples -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.13.5</version>
    </dependency>

    <!-- Required for Function primitives -->
    <dependency>
      <groupId>com.google.cloud.functions</groupId>
      <artifactId>functions-framework-api</artifactId>
      <version>1.1.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <!--
          Google Cloud Functions Framework Maven plugin

          This plugin allows you to run Cloud Functions Java code
          locally. Use the following terminal command to run a
          given function locally:

          mvn function:run -Drun.functionTarget=your.package.yourFunction
        -->
        <groupId>com.google.cloud.functions</groupId>
        <artifactId>function-maven-plugin</artifactId>
        <version>0.11.0</version>
        <configuration>
          <functionTarget>functions.ScalaHelloWorld</functionTarget>
        </configuration>
      </plugin>
      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>4.4.1</version>
        <executions>
          <execution>
            <id>scala-compile</id>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Implementa las funciones de HTTP

Kotlin

gcloud functions deploy kotlin-helloworld --entry-point functions.KotlinHelloWorld --runtime java17 --trigger-http --allow-unauthenticated --memory 512MB

Groovy

gcloud functions deploy groovy-helloworld --entry-point functions.GroovyHelloWorld --runtime java17 --trigger-http --allow-unauthenticated --memory 512MB

Scala

gcloud functions deploy scala-helloworld --entry-point functions.ScalaHelloWorld --runtime java17 --trigger-http --allow-unauthenticated --memory 512MB

Ejemplos de funciones controladas por eventos

Debes usar funciones controladas por eventos cuando quieres que se invoque la función de Cloud Functions de forma indirecta en respuesta a un evento asíncrono, como un mensaje en un tema de Pub/Sub, un cambio en un bucket de Cloud Storage o un evento de Firebase.

Existen dos tipos de funciones controladas por eventos: funciones en segundo plano y funciones de CloudEvent. Los lenguajes JVM solo admiten funciones en segundo plano.

Kotlin

import com.google.cloud.functions.BackgroundFunction
import com.google.cloud.functions.Context
import functions.eventpojos.PubsubMessage
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.logging.Logger

class KotlinHelloPubSub : BackgroundFunction<PubsubMessage> {
    override fun accept(message: PubsubMessage, context: Context) {
        // name's default value is "world"
        var name = "world"
        if (message?.data != null) {
            name = String(
                    Base64.getDecoder().decode(message.data!!.toByteArray(StandardCharsets.UTF_8)),
                    StandardCharsets.UTF_8)
        }
        LOGGER.info(String.format("Hello %s!", name))
        return;
    }

    companion object {
        private val LOGGER = Logger.getLogger(KotlinHelloPubSub::class.java.name)
    }
}

Cuando desarrollas funciones en segundo plano, debes definir las clases para los eventos que activan tus funciones. Sin embargo, es posible que la serialización de GSON no funcione de inmediato para Kotlin si tu clase de evento no sigue ciertos lineamientos.

En tu clase de evento Kotlin, las propiedades deben cumplir con estos lineamientos:

  • Se pueden configurar en null.
  • No tienen un valor predeterminado asignado.
  • No son propiedades delegadas.

Otro enfoque es crear tus clases de eventos en Java y usarlas desde tu clase de función de Kotlin.

Groovy

import com.google.cloud.functions.BackgroundFunction
import com.google.cloud.functions.Context
import functions.eventpojos.PubsubMessage
import java.nio.charset.StandardCharsets
import java.util.logging.Logger

class GroovyHelloPubSub implements BackgroundFunction<PubsubMessage> {
    private static final Logger LOGGER = Logger.getLogger(GroovyHelloPubSub.class.name)

    @Override
    void accept(PubsubMessage message, Context context) {
        // name's default value is "world"
        String name = "world"

        if (message?.data) {
             name = new String(Base64.decoder.decode(message.data), StandardCharsets.UTF_8)
        }

        LOGGER.info("Hello ${name}!")
        return
    }
}

Scala

import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.logging.Logger

import com.google.cloud.functions.{BackgroundFunction, Context}
import functions.eventpojos.PubsubMessage

class ScalaHelloPubSub extends BackgroundFunction[PubsubMessage] {

  val LOGGER = Logger.getLogger(this.getClass.getName)

  override def accept(message: PubsubMessage, context: Context): Unit = {
    // name's default value is "world"
    var name = "world"
    if (message != null && message.getData != null) {
      name = new String(Base64.getDecoder.decode(
        message.getData.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8)
    }
    LOGGER.info(String.format("Hello %s!", name))
  }
}

Implementa las funciones en segundo plano

Kotlin

gcloud functions deploy kotlin-hello-pubsub --entry-point functions.KotlinHelloPubSub --runtime java17 --trigger-topic my-topic --allow-unauthenticated --memory 512MB

Groovy

gcloud functions deploy groovy-hello-pubsub --entry-point functions.GroovyHelloPubSub --runtime java17 --trigger-topic my-topic --allow-unauthenticated --memory 512MB

Scala

gcloud functions deploy scala-hello-pubsub --entry-point functions.ScalaHelloPubSub --runtime java17 --trigger-topic my-topic --allow-unauthenticated --memory 512MB

Prueba los ejemplos en segundo plano

Puedes probar los ejemplos en segundo plano de la siguiente manera:

  1. Publica un mensaje en tu tema de Pub/Sub para activar tu función:

    gcloud pubsub topics publish my-topic --message Flurry
  2. Consulta los registros:

    gcloud functions logs read --limit 10

Deberías ver algo como esto, con un mensaje que incluya el nombre que publicaste en el tema de Pub/Sub:

D      my-function  ...  Function execution started
I      my-function  ...  Hello Flurry!
D      my-function  ...  Function execution took 39 ms, finished with status: 'ok'