Opções de implementação do Java

Tem duas opções para implementar uma função Java:

Implemente a partir da origem

O código-fonte da sua função tem de estar no local habitual para projetos Maven (src/main/java). As funções de exemplo neste documento estão diretamente em src/main/java, sem declaração de pacote no ficheiro de origem .java. Para código não trivial, é provável que introduza um pacote. Se esse pacote for com.example, a hierarquia teria o seguinte aspeto:

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

Use o seguinte comando para implementar uma função HTTP:

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

Onde:

  • $name é um nome descritivo arbitrário que será o nome da função após a implementação. $name só pode conter letras, números, sublinhados e hífenes.
  • $function_class é o nome totalmente qualificado da sua classe (por exemplo, com.example.MyFunction ou apenas MyFunction se não usar um pacote).

Use o seguinte comando para implementar uma função orientada por eventos:

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

Onde:

  • $name é um nome descritivo arbitrário que será o nome da função após a implementação.
  • $function_class é o nome totalmente qualificado da sua classe (por exemplo, com.example.MyFunction ou apenas MyFunction se não usar um pacote).
  • $resource_name e $event_name são específicos dos eventos que acionam a sua função. Alguns exemplos de recursos e eventos suportados são o Google Cloud Pub/Sub e o Google Cloud Storage.

Quando implementa uma função a partir da origem, a CLI gcloud carrega o diretório de origem (e tudo o que contém) para o Google Cloud para compilar. Para evitar o envio de ficheiros desnecessários, pode usar o ficheiro .gcloudignore. Edite o ficheiro .gcloudignore para ignorar diretórios comuns, como .git e target/. Por exemplo, um ficheiro .gcloudignore pode conter o seguinte:

.git
target
build
.idea

Implemente a partir de um JAR

Pode implementar um JAR pré-criado que contenha a função. Isto é útil, especialmente, se precisar de implementar uma função que use dependências de um repositório de artefactos privado que não pode ser acedido a partir do pipeline de compilação do Google Cloud quando compila a partir da origem. O JAR pode ser um JAR abrangente que contém a classe de função e todas as respetivas classes de dependência, ou um JAR simples que tem entradas Class-Path para JARs de dependência no ficheiro META-INF/MANIFEST.MF.

Crie e implemente um JAR Uber

Um JAR uber é um ficheiro JAR que contém as classes de funções, bem como todas as respetivas dependências. Pode criar um JAR completo com o Maven e o Gradle. Para implementar um Uber JAR, tem de ser o único ficheiro JAR no respetivo diretório, por exemplo:

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

Pode copiar o ficheiro para esta estrutura de diretórios ou usar os plug-ins Maven e Gradle para gerar o diretório de implementação correto.

Maven

Use o plug-in Maven Shade para criar um JAR uber. Configure o seu pom.xml com o plug-in 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>

Crie o JAR Uber:

mvn package

Em seguida, implemente com o seguinte comando:

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

Gradle

Use o plugin Shadow para o Gradle. Configure o plugin no ficheiro 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()
}
...

Agora, pode executar o Gradle com o comando shadowJar:

gradle shadowJar

Em seguida, implemente com o seguinte comando:

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

Crie e implemente um JAR simples com dependências externas

Pode criar e implementar um ficheiro JAR simples em vez de um uber JAR. Um JAR simples é um ficheiro JAR que contém apenas as classes de funções sem as dependências incorporadas no mesmo ficheiro JAR. Uma vez que as dependências ainda são necessárias para a implementação, tem de configurar as coisas da seguinte forma:

  • As dependências têm de estar numa subdiretoria relativa ao JAR a implementar.
  • O JAR tem de ter um ficheiro META-INF/MANIFEST.MF que inclua um atributo Class-Path cujo valor liste os caminhos de dependência necessários.

Por exemplo, o seu ficheiro JAR my-function.jar tem um ficheiro META-INF/MANIFEST.MF com 2 dependências no diretório libs/ (uma lista de caminhos relativos separados por espaços):

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

O diretório de implementação deve conter o ficheiro JAR da função principal, bem como um subdiretório com as duas dependências das quais a função depende:

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

Pode criar um JAR simples com o Maven e o Gradle:

Maven

Use o plug-in JAR do Maven para configurar automaticamente MANIFEST.MF com os caminhos para as dependências e, de seguida, use o plug-in de dependência do Maven para copiar as dependências.

<?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>

Crie o JAR simples:

mvn package

Em seguida, implemente com o seguinte comando:

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

Gradle

Atualize o ficheiro do projeto build.gradle para adicionar uma nova tarefa para obter as dependências:

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
 }
}