Reembale um ficheiro WAR num ficheiro JAR

Este documento descreve como reempacotar uma aplicação Java 8 como um ficheiro JAR para execução num tempo de execução Java suportado. Para usar um tempo de execução Java suportado, pode incorporar um servidor como o Jetty ou colocar a sua aplicação num contentor com o Docker para um tempo de execução personalizado sem reescrever completamente a aplicação. Pode executar as suas aplicações WAR existentes em plataformas Java modernas ou ambientes de nuvem flexíveis. Escolha um dos seguintes métodos que melhor se adequa à sua estratégia de implementação e infraestrutura:

Prepare a sua aplicação Web Java 8 (ficheiro WAR)

Antes de reempacotar a sua aplicação Java 8 como um ficheiro JAR suportado, tem de criar um ficheiro WAR. Esta secção fornece uma aplicação Java 8 de exemplo que cria um ficheiro WAR. Siga as instruções para criar uma aplicação Java 8 hello-world:

  1. Crie um ficheiro HelloServlet.java no diretório de origem:

    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/hello")
    public final class HelloServlet extends HttpServlet {
      /**
       * This method handles GET requests to the /hello endpoint.
       *
       * <p>Subclasses should not override this method.
       *
       * @param request the HttpServletRequest object
       * @param response the HttpServletResponse object
       * @throws ServletException if a servlet-specific error occurs
       * @throws IOException if an I/O error occurs
       */
      @Override
      protected void doGet(
          final HttpServletRequest request, final HttpServletResponse response)
          throws ServletException, IOException {
        response.setContentType("text/html");
        response.getWriter().println("<h1>Hello, World!</h1>");
      }
    }
  2. Crie um ficheiro de descritor de implementação web.xml para configurar a sua aplicação Web:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    
    </web-app>
  3. Crie uma página de destino index.jsp:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Hello App Engine</title>
    </head>
    <body>
        <h1>Welcome to Google App Engine!</h1>
        <p><a href="hello">Say Hello</a></p>
    </body>
    </html>
    
  4. Adicione o seguinte código no ficheiro pom.xml para definir a compilação da sua aplicação Java 8:

    • Configuração da embalagem WAR:

      <groupId>com.example</groupId>
      <artifactId>HelloWorldApp</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
    • maven-war-plugin com a origem maven.compiler e o destino definidos para a versão 1.8:

      <properties>
          <maven.compiler.source>1.8</maven.compiler.source>
          <maven.compiler.target>1.8</maven.compiler.target>
          <java.version>8</java.version>
      </properties>
      
    • javax.servlet-api dependência:

      <dependencies>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>3.1.0</version>
              <scope>provided</scope>
          </dependency>
      </dependencies>
      
    • Configuração do Maven:

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version> <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
      
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.3.2</version>
                </plugin>
      
            </plugins>
        </build>
      

    O diretório do projeto deve ser semelhante à seguinte estrutura:

    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
    
  5. Execute mvn install no diretório do projeto da sua aplicação para gerar o ficheiro WAR HelloWorldApp-1.0.war no diretório de destino.

Use ficheiros Docker para implementar a sua aplicação (recomendado)

Os tempos de execução personalizados são adequados para plataformas que suportam contentores personalizados, como os tempos de execução personalizados do App Engine. Os tempos de execução personalizados oferecem flexibilidade, permitindo-lhe configurar o ambiente de tempo de execução. Para ver um exemplo passo a passo da implementação de tempos de execução personalizados, consulte o artigo Crie uma app de tempo de execução personalizado no ambiente flexível do App Engine.

As instruções seguintes descrevem como colocar a sua aplicação Java 8 num contentor usando um Dockerfile:

  1. Prepare a sua aplicação Web Java 8 (ficheiro WAR)
  2. Crie a imagem de contentor e envie-a para o Artifact Registry
  3. Implemente a sua aplicação

Crie a imagem de contentor e envie-a para o Artifact Registry

Esta secção descreve como criar uma imagem do Docker com o Cloud Build e enviá-la para um repositório do Artifact Registry. Siga estes passos para criar uma imagem de contentor da sua aplicação:

  1. Crie um ficheiro cloudbuild.yaml no diretório de origem para criar a imagem do Docker e enviá-la para o Artifact Registry:

    steps:
    # Step 1: Build the Docker image
    - name: "gcr.io/cloud-builders/docker"
        args:
        - "build"
        - "-t"
        - "$LOCATION-docker.pkg.dev/$PROJECT/$REPOSITORY/SERVICE:VERSION"
        - "."
    
    # Step 2: Push the Docker image to Artifact Registry
    - name: "gcr.io/cloud-builders/docker"
        args:
        - "push"
        - "$LOCATION-docker.pkg.dev/$PROJECT/$REPOSITORY/SERVICE:VERSION"
    
    images:
    - "$LOCATION-docker.pkg.dev/$PROJECT/$REPOSITORY/SERVICE:VERSION"
    

    Substituição:

    • LOCATION com a Google Cloud região onde implementa a sua app.
    • PROJECT com o ID do seu Google Cloud projeto.
    • REPOSITORY com o nome do seu repositório do Artifact Registry.
    • IMAGE com o URL da imagem do contentor.
    • TAG com a etiqueta de imagem do contentor.
  2. Crie um Dockerfile com a seguinte configuração:

    
    # Use Maven to build the project with JDK 8
    FROM maven:3.8.6-openjdk-8 AS build
    
    # Set working directory
    WORKDIR /app
    
    # Copy the application source code
    COPY . .
    
    # Build the application
    RUN mvn clean package
    
    # Use Jetty as the runtime
    FROM jetty:9.4-jdk8
    
    # Set Jetty working directory
    WORKDIR /var/lib/jetty/webapps
    
    # Copy the built WAR file
    COPY --from=build /app/target/*.war ./ROOT.war
    
    # Expose the default Jetty port
    EXPOSE 8080
    
    # Start Jetty correctly
    CMD ["java", "-Djetty.base=/var/lib/jetty", "-jar", "/usr/local/jetty/start.jar"]
  3. Transfira e instale o Docker para testar a app de exemplo e execute o contentor Hello World na sua máquina local.

  4. Crie a imagem de contentor e envie-a para o Artifact Registry:

    gcloud builds submit .
    

Implemente a sua aplicação

Para implementar a sua aplicação do App Engine:

  1. Configure o ficheiro app.yaml para usar um tempo de execução personalizado no diretório de origem:

    runtime: custom
    env: flex
    instance_class: F1
    
    handlers:
      - url: /.*
        script: auto

    O diretório do projeto deve ser semelhante à seguinte estrutura:

    ├── Dockerfile
    ├── README.md
    ├── app.yaml
    ├── cloudbuild.yaml
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
  2. Implemente a sua aplicação através do comando gcloud app deploy:

    gcloud app deploy --image-url=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:tag
    

    Substituição:

    • LOCATION com a Google Cloud região onde implementa a sua app.
    • PROJECT com o ID do seu Google Cloud projeto.
    • REPOSITORY com o nome do seu repositório do Artifact Registry.
    • IMAGE com o URL da imagem do contentor.
    • TAG com a etiqueta de imagem do contentor.

Use um tempo de execução Java incorporado

As instruções seguintes demonstram como reempacotar uma aplicação Java 8 do App Engine com um servidor incorporado (Jetty) para ser executada como um JAR autónomo num tempo de execução Java suportado:

  1. Crie um servidor Jetty incorporado
  2. Prepare a sua aplicação Web Java 8 (ficheiro WAR)
  3. Execute o ficheiro WAR com o Jetty incorporado e implemente a sua aplicação

Crie um servidor Jetty incorporado

Para agrupar o ficheiro WAR da sua aplicação com um servidor Jetty incorporado, siga estes passos:

  1. Crie uma classe Main para inicializar e configurar o servidor Jetty para executar o seu ficheiro WAR. A classe Main configura a porta do servidor que tem como predefinição 8080. Também pode modificar o código fonte para usar uma porta especificada na variável de ambiente.PORT A classe Main configura o controlador WebAppContext para disponibilizar o seu ficheiro WAR:

    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.webapp.Configuration.ClassList;
    import org.eclipse.jetty.webapp.WebAppContext;
    
    /** Simple Jetty Main that can execute a WAR file when passed as an argument. */
    public class Main {
    
      public static void main(String[] args) throws Exception {
        if (args.length != 1) {
          System.err.println("Usage: need a relative path to the war file to execute");
          System.exit(1);
        }
        System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StrErrLog");
        System.setProperty("org.eclipse.jetty.LEVEL", "INFO");
    
        // Create a basic Jetty server object that will listen on port defined by
        // the PORT environment variable when present, otherwise on 8080.
        int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
        Server server = new Server(port);
    
        // The WebAppContext is the interface to provide configuration for a web
        // application. In this example, the context path is being set to "/" so
        // it is suitable for serving root context requests.
        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        webapp.setWar(args[0]);
        ClassList classlist = ClassList.setServerDefault(server);
    
        // Enable Annotation Scanning.
        classlist.addBefore(
            "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
            "org.eclipse.jetty.annotations.AnnotationConfiguration");
    
        // Set the the WebAppContext as the ContextHandler for the server.
        server.setHandler(webapp);
    
        // Start the server! By using the server.join() the server thread will
        // join with the current thread. See
        // "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()"
        // for more details.
        server.start();
        server.join();
      }
    }
  2. Crie o ficheiro do projeto Maven pom.xml e adicione a seguinte configuração:

    1. Defina as propriedades maven.compiler.source e maven.compiler.target para um tempo de execução Java suportado:

      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <jetty.version>9.4.57.v20241219</jetty.version>
      </properties>
    2. Adicione dependências para o Jetty:

      <dependencies>
        <!-- Embedded Jetty dependencies -->
        <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>jetty-server</artifactId>
          <version>${jetty.version}</version>
        </dependency>
        <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>jetty-webapp</artifactId>
          <version>${jetty.version}</version>
          <type>jar</type>
        </dependency>
        <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>jetty-util</artifactId>
          <version>${jetty.version}</version>
        </dependency>
        <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>jetty-annotations</artifactId>
          <version>${jetty.version}</version>
        </dependency>
        <!-- extra explicit dependency needed because there is a JSP in the sample-->
        <dependency>
          <groupId>org.eclipse.jetty</groupId>
          <artifactId>apache-jsp</artifactId>
          <version>${jetty.version}</version>
        </dependency>
      </dependencies>
    3. Configure a propriedade maven-assembly-plugin para criar dependências de pacotes:

      
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <finalName>jetty</finalName>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
            <manifest>
              <mainClass>com.example.appengine.jetty.Main</mainClass>
            </manifest>
          </archive>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      

    O diretório do projeto deve ser semelhante à seguinte estrutura:

    ├─src
    │  └─main
    │      └─java
    │          └─jetty
    │              └─Main.java
    └─pom.xml
    
  3. Execute o comando mvn install no diretório do projeto do Jetty Runner. Isto gera o ficheiro jetty-jar-with-dependencies.jar no diretório de destino.

  4. Siga as instruções na secção Prepare a sua aplicação Web Java 8 (ficheiro WAR) para criar um ficheiro WAR.

Execute o ficheiro WAR com o Jetty incorporado e implemente a sua aplicação

Esta secção fornece passos para criar um pacote da sua aplicação num ficheiro JAR executável. Siga estas instruções para criar o pacote e implementar a sua aplicação:

  1. Coloque o JAR do Jetty Runner gerado jetty-jar-with-dependencies.jar e o ficheiro WAR da sua aplicação HelloWorldApp-1.0.war no mesmo diretório.

  2. Execute a aplicação com um runtime Java suportado:

        java -jar jetty-jar-with-dependencies.jar HelloWorldApp-1.0.war
    
    1. No navegador de Internet, navegue para http://localhost:8080. Deverá ver a página de boas-vindas da aplicação.
  3. Crie um elemento entrypoint no ficheiro app.yaml para chamar o ficheiro jetty-jar-with-dependencies e transmita o ficheiro WAR como um argumento. A versão especificada no ficheiro WAR tem de ser a mesma versão que a do ficheiro pom.xml:

    runtime: java
    env: flex
    runtime_config:
      operating_system: ubuntu22
      runtime_version: 21
    entrypoint: "java -jar jetty-jar-with-dependencies.jar sample.war"
    handlers:
      - url: /.*
        script: this field is required, but ignored
    
    manual_scaling:
      instances: 1
  4. Implemente a sua aplicação através do comando gcloud app deploy.