Réempaqueter un fichier WAR dans un fichier JAR

Ce document explique comment réempaqueter une application Java 8 en tant que fichier JAR à exécuter sur un environnement d'exécution Java compatible. Pour utiliser un environnement d'exécution Java compatible, vous pouvez intégrer un serveur tel que Jetty ou conteneuriser votre application avec Docker pour un environnement d'exécution personnalisé sans réécrire complètement votre application. Vous pouvez exécuter vos applications WAR existantes sur des plates-formes Java modernes ou des environnements cloud flexibles. Choisissez parmi les méthodes suivantes celle qui correspond le mieux à votre stratégie de déploiement et à votre infrastructure :

Préparer votre application Web Java 8 (fichier WAR)

Avant de réempaqueter votre application Java 8 en tant que fichier JAR compatible, vous devez créer un fichier WAR. Cette section fournit un exemple d'application Java 8 qui crée un fichier WAR. Suivez les instructions pour créer une application Java 8 hello-world :

  1. Créez un fichier HelloServlet.java dans votre répertoire source :

    
    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. Créez un fichier descripteur de déploiement web.xml pour configurer votre application 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. Créez une page de destination 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. Ajoutez le code suivant dans le fichier pom.xml pour définir le build de votre application Java 8 :

    • Configuration de l'empaquetage WAR :

      <groupId>com.example</groupId>
      <artifactId>HelloWorldApp</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
    • Plug-in maven-war-plugin avec la source et la cible maven.compiler définies sur la version 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>
      
    • Dépendance javax.servlet-api :

      <dependencies>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>3.1.0</version>
              <scope>provided</scope>
          </dependency>
      </dependencies>
      
    • Configuration 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>
      

    Le répertoire de votre projet doit ressembler à la structure suivante :

    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
    
  5. Exécutez mvn install dans le répertoire du projet de votre application pour générer le fichier WAR HelloWorldApp-1.0.war dans le répertoire cible.

Utiliser des fichiers Dockerfile pour déployer votre application (recommandé)

Les environnements d'exécution personnalisés conviennent aux plates-formes compatibles avec les conteneurs personnalisés, comme les environnements d'exécution personnalisés App Engine. Les environnements d'exécution personnalisés offrent de la flexibilité en vous permettant de configurer l'environnement d'exécution. Pour obtenir un exemple de procédure de déploiement d'environnements d'exécution personnalisés, consultez Créer une application d'exécution personnalisée dans l'environnement flexible App Engine.

Les instructions suivantes décrivent comment conteneuriser votre application Java 8 à l'aide d'un fichier Dockerfile :

  1. Préparer votre application Web Java 8 (fichier WAR)
  2. Créer l'image de conteneur et la transférer vers Artifact Registry
  3. Déployer votre application

Créer l'image de conteneur et la transférer vers Artifact Registry

Cette section explique comment créer une image Docker à l'aide de Cloud Build et la transférer vers un dépôt Artifact Registry. Pour créer une image de conteneur de votre application, procédez comme suit :

  1. Créez un fichier cloudbuild.yaml dans votre répertoire source pour créer l'image Docker et la transférer vers 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"
    

    Remplacez :

    • LOCATION par la région Google Cloud dans laquelle vous déployez votre application.
    • PROJECT par l'ID de votre projet Google Cloud .
    • REPOSITORY par le nom de votre dépôt Artifact Registry.
    • IMAGE par l'URL de votre image de conteneur.
    • TAG par le tag de votre image de conteneur.
  2. Créez un fichier Dockerfile avec la configuration suivante :

    
    # 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. Téléchargez et installez Docker pour tester votre exemple d'application et exécuter le conteneur Hello World sur votre ordinateur local.

  4. Créez l'image de conteneur et transférez-la vers Artifact Registry :

    gcloud builds submit .
    

Déployer votre application

Pour déployer votre application App Engine :

  1. Configurez votre fichier app.yaml pour utiliser un environnement d'exécution personnalisé dans le répertoire source :

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

    Le répertoire de votre projet doit ressembler à la structure suivante :

    ├── Dockerfile
    ├── README.md
    ├── app.yaml
    ├── cloudbuild.yaml
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
  2. Déployez votre application à l'aide de la commande gcloud app deploy :

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

    Remplacez :

    • LOCATION par la région Google Cloud dans laquelle vous déployez votre application.
    • PROJECT par l'ID de votre projet Google Cloud .
    • REPOSITORY par le nom de votre dépôt Artifact Registry.
    • IMAGE par l'URL de votre image de conteneur.
    • TAG par le tag de votre image de conteneur.

Utiliser un environnement d'exécution Java intégré

Les instructions suivantes montrent comment réempaqueter une application Java 8 App Engine avec un serveur intégré (Jetty) pour l'exécuter en tant que fichier JAR autonome sur un environnement d'exécution Java compatible :

  1. Créer un serveur Jetty intégré
  2. Préparer votre application Web Java 8 (fichier WAR)
  3. Exécuter le fichier WAR avec Jetty intégré et déployer votre application

Créer un serveur Jetty intégré

Pour regrouper le fichier WAR de votre application avec un serveur Jetty intégré, procédez comme suit :

  1. Créez une classe Main pour initialiser et configurer le serveur Jetty afin d'exécuter votre fichier WAR. La classe Main configure le port du serveur, qui est défini par défaut sur 8080. Vous pouvez également modifier le code source pour utiliser un port spécifié dans la variable d'environnement PORT. La classe Main configure le gestionnaire WebAppContext pour diffuser votre fichier 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. Créez le fichier de projet Maven pom.xml et ajoutez la configuration suivante :

    1. Définissez les propriétés maven.compiler.source et maven.compiler.target sur un environnement d'exécution Java compatible :

      <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. Ajoutez des dépendances pour 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. Configurez la propriété maven-assembly-plugin pour les dépendances du package :

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

    Le répertoire de votre projet doit ressembler à la structure suivante :

    ├─src
    │  └─main
    │      └─java
    │          └─jetty
    │              └─Main.java
    └─pom.xml
    
  3. Exécutez la commande mvn install dans le répertoire du projet Jetty Runner. Cela génère le jetty-jar-with-dependencies.jar dans votre répertoire cible.

  4. Suivez les instructions de la section Préparer votre application Web Java 8 (fichier WAR) pour créer un fichier WAR.

Exécuter le fichier WAR avec Jetty intégré et déployer votre application

Cette section explique comment empaqueter votre application dans un fichier JAR exécutable. Suivez ces instructions pour empaqueter et déployer votre application :

  1. Placez le fichier JAR du programme d'exécution Jetty généré jetty-jar-with-dependencies.jar et le fichier WAR de votre application HelloWorldApp-1.0.war dans le même répertoire.

  2. Exécutez l'application à l'aide d'un environnement d'exécution Java compatible :

        java -jar jetty-jar-with-dependencies.jar HelloWorldApp-1.0.war
    
    1. Dans votre navigateur Web, accédez à http://localhost:8080. La page de bienvenue de votre application devrait s'afficher.
  3. Créez un élément entrypoint dans votre fichier app.yaml pour appeler le fichier jetty-jar-with-dependencies et transmettre votre fichier WAR en tant qu'argument. La version que vous spécifiez dans le fichier WAR doit être identique à celle du fichier 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. Déployez votre application à l'aide de la commande gcloud app deploy.