Opciones de implementación de Java

Tienes dos opciones para implementar una función de Java:

Implementa desde la fuente

El código fuente de la función debe estar en la ubicación habitual para proyectos de Maven (src/main/java). Las funciones de muestra de este documento están directamente en src/main/java, sin declaración de paquete en el archivo de origen .java. En el caso de código no trivial, se recomienda ingresar un paquete. Si ese paquete es com.example, tu jerarquía debería verse de la siguiente manera:

myfunction/
├─ pom.xml
├─ src
    ├─main
        ├─ java
            ├─ com
                ├─ example
                    ├─ MyFunction.java

Usa el siguiente comando para implementar una función de HTTP:

gcloud functions deploy $name --trigger-http \
    --entry-point $function_class --runtime java17

En el ejemplo anterior, se ilustra lo siguiente:

  • $name es un nombre arbitrario y descriptivo que será el nombre de la función una vez implementada. $name solo puede contener letras, números, guiones bajos y guiones.
  • $function_class es el nombre completamente calificado de tu clase (por ejemplo, com.example.MyFunction o solo MyFunction si no usas un paquete).

Usa el siguiente comando para implementar una función en segundo plano:

gcloud functions deploy $name --entry-point $function_class \
    --trigger-resource $resource_name \
    --trigger-event $event_name \
    --runtime java17

En el ejemplo anterior, se ilustra lo siguiente:

  • $name es un nombre arbitrario y descriptivo que será el nombre de la función una vez implementada.
  • $function_class es el nombre completamente calificado de tu clase (por ejemplo, com.example.MyFunction o solo MyFunction si no usas un paquete).
  • $resource_name y $event_name son específicos de los eventos que activan la función en segundo plano. Los ejemplos de recursos y eventos compatibles son Google Cloud Pub/Sub y Google Cloud Storage.

Cuando implementas una función desde la fuente, Google Cloud CLI sube el directorio del código fuente (y todo su contenido) a Google Cloud para su compilación. Para evitar enviar archivos innecesarios, puedes usar el archivo .gcloudignore. Edita el archivo .gcloudignore para ignorar los directorios comunes, como .git y target/. Por ejemplo, un archivo .gcloudignore puede contener lo siguiente:

.git
target
build
.idea

Implementa desde un archivo JAR

Puedes implementar un JAR ya compilado que contenga la función. Esto es útil, en especial, si necesitas implementar una función que usa dependencias de un repositorio de artefactos privados al que no se puede acceder desde la canalización de compilación de Google Cloud cuando compilas desde la fuente. El JAR puede ser un uber JAR que contenga la clase de función y todas sus clases de dependencias o un JAR delgado que tenga entradas Class-Path para los archivos JAR de dependencias en el archivo META-INF/MANIFEST.MF.

Compila e implementa un archivo uber JAR

Un uber JAR es un archivo JAR que contiene las clases de función y todas sus dependencias. Puedes compilar un archivo uber JAR con Maven y Gradle. Para implementar un uber JAR, este debe ser el único archivo JAR en su propio directorio, por ejemplo:

my-function-deployment/
 ├─ my-function-with-all-dependencies.jar

Puedes copiar el archivo en esta estructura de directorio o usar los complementos de Maven y Gradle para generar el directorio de implementación correcto.

Maven

Usa el complemento Maven Shade para compilar un archivo uber JAR. Configura el archivo pom.xml con el complemento Shade:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
            <configuration>
              <outputFile>${project.build.directory}/deployment/${build.finalName}.jar</outputFile>
              <transformers>
                <!-- This may be needed if you need to shade a signed JAR -->
                <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
                  <resource>.SF</resource>
                  <resource>.DSA</resource>
                  <resource>.RSA</resource>
                </transformer>
                <!-- This is needed if you have dependencies that use Service Loader. Most Google Cloud client libraries does. -->
                <transformer implementation=
       "org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Compila el archivo uber JAR:

mvn package

Luego, realiza una implementación con el siguiente comando:

gcloud functions deploy jar-example \
    --entry-point=Example \
    --runtime=java17 \
    --trigger-http \
    --source=target/deployment

Gradle

Usa el complemento Shadow para Gradle. Configura el complemento en el archivo build.gradle:

buildscript {
   repositories {
       jcenter()
   }
   dependencies {
       ...
       classpath "com.github.jengelman.gradle.plugins:shadow:5.2.0"
   }
}

plugins {
   id 'java'
   ...
}
sourceCompatibility = '17.0'
targetCompatibility = '17.0'
apply plugin: 'com.github.johnrengelman.shadow'

shadowJar {
   mergeServiceFiles()
}
...

Ahora puedes ejecutar Gradle con el comando shadowJar:

gradle shadowJar

Luego, realiza una implementación con el siguiente comando:

gcloud functions deploy jar-example \
   --entry-point=Example \
   --runtime=java17 \
   --trigger-http \
   --source=build/libs

Compila e implementa un archivo JAR delgado con dependencias externas

Puedes compilar e implementar un archivo JAR delgado en lugar de un uber JAR. Un JAR delgado es un archivo JAR que contiene solo las clases de función sin las dependencias incorporadas en el mismo archivo JAR. Debido a que las dependencias aún son necesarias para la implementación, debes configurar lo siguiente:

  • Las dependencias deben estar en un subdirectorio relacionado con el archivo JAR para que se implemente.
  • El JAR debe tener un archivo META-INF/MANIFEST.MF que incluya un atributo Class-Path cuyo valor enumere las rutas de dependencias requeridas.

Por ejemplo, tu archivo JAR my-function.jar tiene un archivo META-INF/MANIFEST.MF con 2 dependencias en el directorio libs/ (una lista separada por espacios de las rutas relativas):

Manifest-Version: 1.0
Class-Path: libs/dep1.jar libs/dep2.jar

El directorio de implementación debe contener el archivo JAR de la función principal y un subdirectorio con las dos dependencias de las que depende la función:

function-deployment/
├─ my-function.jar
├─ libs
       ├─ dep1.jar
       ├─ dep2.jar

Puedes compilar un JAR delgado con Maven y Gradle:

Maven

Usa el complemento JAR de Maven a fin de configurar de forma automática MANIFEST.MF con las rutas a las dependencias y, luego, usa el complemento Maven Dependency para copiar las dependencias.

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  ...
  <build>
    ...
    <plugins>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <classpathPrefix>libs/</classpathPrefix>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <overWriteReleases>false</overWriteReleases>
              <includeScope>runtime</includeScope>
              <outputDirectory>${project.build.directory}/libs</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-resources</id>
            <phase>package</phase>
            <goals><goal>copy-resources</goal></goals>
            <configuration>
              <outputDirectory>${project.build.directory}/deployment</outputDirectory>
              <resources>
                <resource>
                  <directory>${project.build.directory}</directory>
                  <includes>
                    <include>${build.finalName}.jar</include>
                    <include>libs/**</include>
                  </includes>
                  <filtering>false</filtering>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Compila el JAR delgado:

mvn package

Luego, realiza una implementación con el siguiente comando:

gcloud functions deploy jar-example \
    --entry-point=Example \
    --runtime=java17 \
    --trigger-http \
    --source=target/deployment

Gradle

Actualiza el archivo del proyecto build.gradle para agregar una tarea nueva a fin de recuperar las dependencias:

dependencies {
   // API available at compilation only, but provided at runtime
   compileOnly 'com.google.cloud.functions:functions-framework-api:1.0.1'
   // dependencies needed by the function
   // ...
}

jar {
 manifest {
   attributes(
     "Class-Path": provider {
         configurations.runtimeClasspath
           .collect { "libs/${it.name}" }.join(' ')
     }
   )
 }
}

task prepareDeployment(type: Copy) {
 into("${buildDir}/deployment")
 into('.') {
   from jar
 }
 into('libs') {
   from configurations.runtimeClasspath
 }
}