Ripacchettizzazione di un file WAR in un file JAR

Questo documento descrive come ricompattare un'applicazione Java 8 come file JAR da eseguire su un runtime Java supportato. Per utilizzare un runtime Java supportato, puoi incorporare un server come Jetty o containerizzare l'applicazione con Docker per un runtime personalizzato senza riscrivere completamente l'applicazione. Puoi eseguire le tue applicazioni WAR esistenti su piattaforme Java moderne o ambienti cloud flessibili. Scegli tra i seguenti metodi quello più adatto alla tua strategia di implementazione e alla tua infrastruttura:

Prepara l'applicazione web Java 8 (file WAR)

Prima di ricompattare l'applicazione Java 8 come file JAR supportato, devi creare un file WAR. Questa sezione fornisce un'applicazione Java 8 di esempio che crea un file WAR. Segui le istruzioni per creare un'applicazione Java 8 hello-world:

  1. Crea un file HelloServlet.java nella directory di origine:

    
    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. Crea un file descrittore di deployment web.xml per configurare l'applicazione 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. Crea una pagina di destinazione 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. Aggiungi il seguente codice al file pom.xml per definire la build per l'applicazione Java 8:

    • Configurazione del pacchetto WAR:

      <groupId>com.example</groupId>
      <artifactId>HelloWorldApp</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
    • Plug-in maven-war-plugin con origine e destinazione impostate sulla versione 1.8:maven.compiler

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

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

    La directory del progetto dovrebbe essere simile alla seguente struttura:

    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
    
  5. Esegui mvn install nella directory del progetto dell'applicazione per generare il file WAR HelloWorldApp-1.0.war nella directory di destinazione.

Utilizza i Dockerfile per eseguire il deployment dell'applicazione (opzione consigliata)

I runtime personalizzati sono adatti alle piattaforme che supportano container personalizzati, come i runtime personalizzati di App Engine. I runtime personalizzati offrono flessibilità consentendoti di configurare l'ambiente di runtime. Per un esempio dettagliato del deployment dei runtime personalizzati, consulta Creare un'app con runtime personalizzato nell'ambiente flessibile di App Engine.

Le seguenti istruzioni descrivono come inserire in un container l'applicazione Java 8 utilizzando un Dockerfile:

  1. Prepara l'applicazione web Java 8 (file WAR)
  2. Crea l'immagine container ed eseguine il push su Artifact Registry
  3. Esegui il deployment dell'applicazione

Crea l'immagine container ed eseguine il push in Artifact Registry

Questa sezione descrive come creare un'immagine Docker utilizzando Cloud Build ed eseguirne il push in un repository Artifact Registry. Per creare un'immagine container della tua applicazione:

  1. Crea un file cloudbuild.yaml nella directory di origine per creare l'immagine Docker ed eseguirne il push in 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"
    

    Sostituisci:

    • LOCATION con la regione Google Cloud in cui esegui il deployment dell'app.
    • PROJECT con l'ID progetto Google Cloud .
    • REPOSITORY con il nome del tuo repository Artifact Registry.
    • IMAGE con l'URL dell'immagine del container.
    • TAG con il tag dell'immagine container.
  2. Crea un Dockerfile con la seguente configurazione:

    
    # 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. Scarica e installa Docker per testare l'app di esempio ed eseguire il container Hello World sulla tua macchina locale.

  4. Crea l'immagine container ed eseguine il push su Artifact Registry:

    gcloud builds submit .
    

Esegui il deployment dell'applicazione

Per eseguire il deployment dell'applicazione App Engine:

  1. Configura il file app.yaml per utilizzare un runtime personalizzato nella directory di origine:

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

    La directory del progetto dovrebbe essere simile alla seguente struttura:

    ├── Dockerfile
    ├── README.md
    ├── app.yaml
    ├── cloudbuild.yaml
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
  2. Esegui il deployment dell'applicazione utilizzando il comando gcloud app deploy:

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

    Sostituisci:

    • LOCATION con la regione Google Cloud in cui esegui il deployment dell'app.
    • PROJECT con l'ID progetto Google Cloud .
    • REPOSITORY con il nome del tuo repository Artifact Registry.
    • IMAGE con l'URL dell'immagine del container.
    • TAG con il tag dell'immagine container.

Utilizzare un runtime Java incorporato

Le seguenti istruzioni mostrano come ricompilare un'applicazione App Engine Java 8 con un server incorporato (Jetty) per eseguirla come JAR autonomo in un runtime Java supportato:

  1. Crea un server Jetty incorporato
  2. Prepara l'applicazione web Java 8 (file WAR)
  3. Esegui il file WAR con Jetty incorporato ed esegui il deployment dell'applicazione

Crea un server Jetty incorporato

Per raggruppare il file WAR dell'applicazione con un server Jetty incorporato, segui questi passaggi:

  1. Crea una classe Main per inizializzare e configurare il server Jetty per eseguire il file WAR. La classe Main configura la porta del server, che per impostazione predefinita è 8080. Puoi anche modificare il codice sorgente per utilizzare una porta specificata nella variabile di ambiente PORT. La classe Main configura il gestore WebAppContext per pubblicare il file 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. Crea il file di progetto Maven pom.xml e aggiungi la seguente configurazione:

    1. Imposta le proprietà maven.compiler.source e maven.compiler.target su un runtime Java supportato:

      <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. Aggiungi le dipendenze per 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. Configura la proprietà maven-assembly-plugin per le dipendenze del pacchetto:

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

    La directory del progetto dovrebbe essere simile alla seguente struttura:

    ├─src
    │  └─main
    │      └─java
    │          └─jetty
    │              └─Main.java
    └─pom.xml
    
  3. Esegui il comando mvn install nella directory del progetto Jetty Runner. In questo modo viene generato il jetty-jar-with-dependencies.jar nella directory di destinazione.

  4. Segui le istruzioni nella sezione Prepara l'applicazione web Java 8 (file WAR) per creare un file WAR.

Esegui il file WAR con Jetty incorporato ed esegui il deployment dell'applicazione

Questa sezione fornisce i passaggi per creare un pacchetto dell'applicazione in un file JAR eseguibile. Segui queste istruzioni per creare il pacchetto e il deployment della tua applicazione:

  1. Inserisci il file JAR Jetty Runner generato jetty-jar-with-dependencies.jar e il file WAR dell'applicazione HelloWorldApp-1.0.war nella stessa directory.

  2. Esegui l'applicazione utilizzando un runtime Java supportato:

        java -jar jetty-jar-with-dependencies.jar HelloWorldApp-1.0.war
    
    1. Nel browser web, vai all'indirizzo http://localhost:8080. Dovresti visualizzare la pagina di benvenuto della tua applicazione.
  3. Crea un elemento entrypoint nel file app.yaml per chiamare il file jetty-jar-with-dependencies e passa il file WAR come argomento. La versione specificata nel file WAR deve essere la stessa del file 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. Esegui il deployment dell'applicazione utilizzando il comando gcloud app deploy.