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 komplett neu schreiben zu müssen. Sie können Ihre vorhandenen WAR-Anwendungen auf modernen Java-Plattformen oder flexiblen Cloud-Umgebungen ausführen. Wählen Sie eine der folgenden Methoden aus, die am besten zu Ihrer Bereitstellungsstrategie und Infrastruktur passt:

Java 8-Webanwendung vorbereiten (WAR-Datei)

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, um eine hello-world-Anwendung für Java 8 zu erstellen:

  1. Erstellen Sie im Quellverzeichnis eine Datei HelloServlet.java:

    
    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 Deployment-Deskriptordatei, 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. Erstellen Sie 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:

    • WAR-Verpackungskonfiguration:

      <groupId>com.example</groupId>
      <artifactId>HelloWorldApp</artifactId>
      <version>1.0</version>
      <packaging>war</packaging>
      
    • maven-war-plugin -Plug-in, wobei die Quelle und das Ziel von maven.compiler auf Version 1.8 festgelegt sind:

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

Anwendung mit Dockerfiles bereitstellen (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 einem Dockerfile containerisieren:

  1. Java 8-Webanwendung vorbereiten (WAR-Datei)
  2. Container-Image erstellen und in Artifact Registry bereitstellen
  3. Anwendung bereitstellen

Container-Image erstellen und in Artifact Registry bereitstellen

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 im Quellverzeichnis eine Datei cloudbuild.yaml, um das Docker-Image zu erstellen und in Artifact Registry zu verschieben:

    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 Anwendung bereitstellen.
    • PROJECT durch Ihre Google Cloud Projekt-ID.
    • 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 Ihre Beispielanwendung zu testen und den Hello World-Container auf Ihrem lokalen Computer auszuführen.

  4. Erstellen Sie das Container-Image und verschieben Sie es in Artifact Registry:

    gcloud builds submit .
    

Anwendung bereitstellen

So stellen Sie Ihre App Engine-Anwendung bereit:

  1. Konfigurieren Sie die Datei app.yaml so, dass im Quellverzeichnis eine benutzerdefinierte Laufzeit 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 Ihre Anwendung mit dem gcloud app deploy Befehl 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 Anwendung bereitstellen.
    • PROJECT durch Ihre Google Cloud Projekt-ID.
    • 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, um sie als eigenständige JAR-Datei in einer unterstützten Java Laufzeit auszuführen:

  1. Eingebetteten Jetty-Server erstellen
  2. Java 8-Webanwendung vorbereiten (WAR-Datei)
  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. Die Main-Klasse richtet den Serverport ein, der standardmäßig auf 8080 festgelegt ist. Sie können den Quellcode auch so ändern, dass ein Port verwendet wird, der in der Umgebungsvariablen PORT angegeben ist. Die Main-Klasse konfiguriert den WebAppContext-Handler, um Ihre WAR-Datei bereitzustellen:

    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 maven.compiler.source und maven.compiler.target Eigenschaften 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 die Eigenschaft maven-assembly-plugin, um Abhängigkeiten zu verpacken:

      
      <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 Datei jetty-jar-with-dependencies.jar im Zielverzeichnis generiert.

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

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

In diesem Abschnitt werden die Schritte zum Verpacken Ihrer Anwendung in einer ausführbaren JAR-Datei beschrieben. Folgen Sie dieser Anleitung, um Ihre Anwendung zu verpacken und bereitzustellen:

  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 die Adresse http://localhost:8080 auf. Die Begrüßungsseite Ihrer Anwendung sollte angezeigt werden.
  3. Erstellen Sie in der Datei app.yaml ein entrypoint-Element, um die Datei jetty-jar-with-dependencies aufzurufen, und übergeben Sie die WAR-Datei als Argument. Die in der WAR-Datei angegebene Version 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 Ihre Anwendung mit dem gcloud app deploy Befehl bereit.