WAR-Datei neu in eine JAR-Datei verpacken

In diesem Dokument wird beschrieben, wie Sie eine Java 8-Anwendung als JAR-Datei neu verpacken, um sie in einer unterstützten Java-Laufzeit auszuführen. Wenn Sie eine unterstützte Java-Laufzeit verwenden möchten, können Sie entweder einen Server wie Jetty einbetten oder Ihre Anwendung mit Docker für eine benutzerdefinierte Laufzeit containerisieren, ohne die Anwendung vollständig neu schreiben zu müssen. Sie können Ihre vorhandenen WAR-Anwendungen auf modernen Java-Plattformen oder in flexiblen Cloud-Umgebungen ausführen. Wählen Sie die Methode aus, die am besten zu Ihrer Bereitstellungsstrategie und Infrastruktur passt:

Java 8-Webanwendung (WAR-Datei) vorbereiten

Bevor Sie Ihre Java 8-Anwendung als unterstützte JAR-Datei neu verpacken, müssen Sie eine WAR-Datei erstellen. In diesem Abschnitt finden Sie eine Beispielanwendung für Java 8, mit der eine WAR-Datei erstellt wird. Folgen Sie der Anleitung zum Erstellen einer Java 8-hello-world-Anwendung:

  1. Erstellen Sie eine HelloServlet.java-Datei in Ihrem Quellverzeichnis:

    
    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. Erstellen Sie eine web.xml-Bereitstellungsdeskriptordatei, um Ihre Webanwendung zu konfigurieren:

    <?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. Erstelle eine Landingpage 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. Fügen Sie der Datei pom.xml den folgenden Code hinzu, um den Build für Ihre Java 8-Anwendung zu definieren:

    • Konfiguration für das Verpacken von WAR-Dateien:

      <groupId>com.example</groupId>
      <artifactId>HelloWorldApp</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
    • maven-war-plugin-Plug-in mit der Quelle maven.compiler und dem Ziel 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-Abhängigkeit:

      <dependencies>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>3.1.0</version>
              <scope>provided</scope>
          </dependency>
      </dependencies>
      
    • Maven-Konfiguration:

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

    Ihr Projektverzeichnis sollte in etwa so aussehen:

    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
    
  5. Führen Sie mvn install im Projektverzeichnis Ihrer Anwendung aus, um die WAR-Datei HelloWorldApp-1.0.war im Zielverzeichnis zu generieren.

Dockerfiles zum Bereitstellen Ihrer Anwendung verwenden (empfohlen)

Benutzerdefinierte Laufzeiten eignen sich für Plattformen, die benutzerdefinierte Container unterstützen, z. B. benutzerdefinierte App Engine-Laufzeiten. Benutzerdefinierte Laufzeiten bieten Flexibilität, da Sie die Laufzeitumgebung konfigurieren können. Ein Beispiel für die Bereitstellung benutzerdefinierter Laufzeiten finden Sie unter Benutzerdefinierte Laufzeitanwendung in der flexiblen App Engine-Umgebung erstellen.

In der folgenden Anleitung wird beschrieben, wie Sie Ihre Java 8-Anwendung mit einer Dockerfile in einen Container packen:

  1. Java 8-Webanwendung (WAR-Datei) vorbereiten
  2. Container-Image erstellen und per Push an Artifact Registry übertragen
  3. Anwendung bereitstellen

Container-Image erstellen und per Push an Artifact Registry übertragen

In diesem Abschnitt wird beschrieben, wie Sie mit Cloud Build ein Docker-Image erstellen und in ein Artifact Registry-Repository verschieben. So erstellen Sie ein Container-Image Ihrer Anwendung:

  1. Erstellen Sie eine cloudbuild.yaml-Datei in Ihrem Quellverzeichnis, um das Docker-Image zu erstellen und in Artifact Registry zu übertragen:

    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"
    

    Ersetzen Sie:

    • LOCATION durch die Google Cloud Region, in der Sie Ihre App bereitstellen.
    • PROJECT durch Ihre Google Cloud Projekt-ID.
    • Ersetzen Sie REPOSITORY durch den Namen Ihres Artifact Registry-Repositorys.
    • IMAGE durch die URL Ihres Container-Images.
    • TAG durch das Tag Ihres Container-Images.
  2. Erstellen Sie ein Dockerfile mit der folgenden Konfiguration:

    
    # 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. Laden Sie Docker herunter und installieren Sie es, um die Beispielanwendung zu testen und den Hello World-Container auf Ihrem lokalen Computer auszuführen.

  4. Erstellen Sie das Container-Image und übertragen Sie es per Push in die Artifact Registry:

    gcloud builds submit .
    

Anwendung bereitstellen

So stellen Sie Ihre App Engine-Anwendung bereit:

  1. Konfigurieren Sie die Datei app.yaml so, dass eine benutzerdefinierte Laufzeit im Quellverzeichnis verwendet wird:

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

    Ihr Projektverzeichnis sollte in etwa so aussehen:

    ├── Dockerfile
    ├── README.md
    ├── app.yaml
    ├── cloudbuild.yaml
    ├── pom.xml
    └── src
        └── main
            ├── java
            │   └── com
            │       └── example
            │           └── HelloServlet.java
            └── webapp
                ├── WEB-INF
                │   └── web.xml
                └── index.jsp
    
  2. Stellen Sie die Anwendung mit dem Befehl gcloud app deploy bereit:

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

    Ersetzen Sie:

    • LOCATION durch die Google Cloud Region, in der Sie Ihre App bereitstellen.
    • PROJECT durch Ihre Google Cloud Projekt-ID.
    • Ersetzen Sie REPOSITORY durch den Namen Ihres Artifact Registry-Repositorys.
    • IMAGE durch die URL Ihres Container-Images.
    • TAG durch das Tag Ihres Container-Images.

Eingebettete Java-Laufzeit verwenden

In der folgenden Anleitung wird gezeigt, wie Sie eine App Engine Java 8-Anwendung mit einem eingebetteten Server (Jetty) neu verpacken, damit sie als eigenständiges JAR in einer unterstützten Java-Laufzeit ausgeführt werden kann:

  1. Eingebetteten Jetty-Server erstellen
  2. Java 8-Webanwendung (WAR-Datei) vorbereiten
  3. WAR-Datei mit eingebettetem Jetty ausführen und Anwendung bereitstellen

Eingebetteten Jetty-Server erstellen

So bündeln Sie die WAR-Datei Ihrer Anwendung mit einem eingebetteten Jetty-Server:

  1. Erstellen Sie eine Main-Klasse, um den Jetty-Server zu initialisieren und zu konfigurieren, damit Ihre WAR-Datei ausgeführt werden kann. Mit der Klasse Main wird der Serverport eingerichtet, der standardmäßig auf 8080 festgelegt ist. Sie können den Quellcode auch so ändern, dass ein Port verwendet wird, der in der Umgebungsvariable PORT angegeben ist. Die Klasse Main konfiguriert den WebAppContext-Handler für die Bereitstellung Ihrer WAR-Datei:

    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. Erstellen Sie die Maven-Projektdatei pom.xml und fügen Sie die folgende Konfiguration hinzu:

    1. Legen Sie die Attribute maven.compiler.source und maven.compiler.target auf eine unterstützte Java-Laufzeit fest:

      <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. Fügen Sie Abhängigkeiten für Jetty hinzu:

      <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. Konfigurieren Sie das Attribut maven-assembly-plugin für Paketabhängigkeiten:

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

    Ihr Projektverzeichnis sollte in etwa so aussehen:

    ├─src
    │  └─main
    │      └─java
    │          └─jetty
    │              └─Main.java
    └─pom.xml
    
  3. Führen Sie den Befehl mvn install im Projektverzeichnis des Jetty-Runners aus. Dadurch wird die jetty-jar-with-dependencies.jar in Ihrem Zielverzeichnis generiert.

  4. Folgen Sie der Anleitung im Abschnitt Java 8-Webanwendung (WAR-Datei) vorbereiten, um eine WAR-Datei zu erstellen.

WAR-Datei mit eingebettetem Jetty ausführen und Anwendung bereitstellen

In diesem Abschnitt wird beschrieben, wie Sie Ihre Anwendung in einer ausführbaren JAR-Datei verpacken. So verpacken und stellen Sie Ihre Anwendung bereit:

  1. Platzieren Sie die generierte Jetty-Runner-JAR-Datei jetty-jar-with-dependencies.jar und die WAR-Datei Ihrer Anwendung HelloWorldApp-1.0.war im selben Verzeichnis.

  2. Führen Sie die Anwendung mit einer unterstützten Java-Laufzeit aus:

        java -jar jetty-jar-with-dependencies.jar HelloWorldApp-1.0.war
    
    1. Rufen Sie in Ihrem Webbrowser http://localhost:8080 auf. Sie sollten die Begrüßungsseite Ihrer Anwendung sehen.
  3. Erstellen Sie ein entrypoint-Element in Ihrer app.yaml-Datei, um die jetty-jar-with-dependencies-Datei aufzurufen, und übergeben Sie die WAR-Datei als Argument. Die Version, die Sie in der WAR-Datei angeben, muss mit der Version in der Datei pom.xml übereinstimmen:

    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. Stellen Sie die Anwendung mit dem Befehl gcloud app deploy bereit.