Quarkus: Anwendungs-Upgrades anhand des Beispiels helloworld von JBoss EAP Quickstart

Hallo an alle in diesem Blog und mit euch den vierten Beitrag aus der Quarkus-Reihe! (Registrieren Sie sich übrigens und besuchen Sie unser Webinar „ Dies ist das native Java-Framework von Quarkus - Kubernetes “, das am 27. Mai stattfinden wird. Wir werden zeigen, wie Sie bei Null anfangen oder vorgefertigte Lösungen übertragen können.) Im



vorherigen Beitrag ging es darum, wie Quarkus MicroProfile und Spring kombiniert. Denken Sie daran, dass Quarkus als "ultraschnelles subatomares Java" positioniert ist. Es handelt sich um einen "Kubernetes-orientierten Java-Stack, der von GraalVM und OpenJDK HotSpot geschärft und aus den besten Bibliotheken und Standards zusammengestellt wurde." Heute zeigen wir, wie vorhandene Java-Anwendungen mithilfe von Quarkus-Funktionen mithilfe der helloworld- Anwendung aus dem Schnellstart- Repository der Red Hat JBoss Enterprise Application Platform (JBoss EAP) aktualisiert werdenVerwendung der von Quarkus unterstützten CDI- und Servlet 3-Technologien.

Hierbei ist zu beachten, dass sich sowohl Quarkus als auch JBoss EAP auf die Verwendung von Tools konzentrieren, die den maximalen Standards entsprechen. Sie haben keine App auf JBoss EAP? Kein Problem, es kann mithilfe des Red Hat Application Migration Toolkit problemlos vom aktuellen Anwendungsserver auf JBoss EAP migriert werden . Danach ist die endgültige und funktionierende Version des aktualisierten Codes im Repository github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus im Modul helloworld verfügbar .

Dieser Beitrag wurde unter Verwendung geschrieben Quarkus Tutorials , vor allem Erstellen der ersten Anwendung und Aufbau einer india Executable .

Wir bekommen einen Code


Erstellen Sie zunächst einen lokalen Klon des JBoss EAP-Schnellstart- Repositorys :

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Cloning into 'jboss-eap-quickstarts'...
remote: Enumerating objects: 148133, done.
remote: Total 148133 (delta 0), reused 0 (delta 0), pack-reused 148133
Receiving objects: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, done.
Resolving deltas: 100% (66476/66476), done.
$ cd jboss-eap-quickstarts/helloworld/

Sehen Sie, wie die ursprüngliche Welt funktioniert


Eigentlich ist das Wesentliche dieser Anwendung aus dem Namen ersichtlich, aber wir werden ihren Code streng wissenschaftlich aktualisieren. Schauen Sie sich daher zunächst diese Anwendung in ihrer ursprünglichen Form an.

Erweitern Sie helloworld

1. Öffnen Sie das Terminal und gehen Sie zum Stammverzeichnis des JBoss EAP-Ordners (Sie können es hier herunterladen ), dh in den EAP_HOME-Ordner.

2. Starten Sie den JBoss EAP-Server mit dem Standardprofil:

$ EAP_HOME/bin/standalone.sh

Hinweis: Unter Windows wird das Skript EAP_HOME \ bin \ standalone.bat zum Ausführen verwendet.

Nach einigen Sekunden sollte Folgendes im Protokoll angezeigt werden:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) started in 3315ms - Started 306 of 527 services (321 services are lazy, passive or on-demand)

3. Öffnen Sie den Browser 127.0.0.1 : 8080 und sehen Sie Folgendes:



Feige. 1. JBoss EAP-Homepage.

4. Befolgen Sie die Anweisungen im Handbuch Erstellen und Bereitstellen des Schnellstarts: Stellen Sie helloworld bereit und führen Sie (aus dem Projektstammordner) den folgenden Befehl aus:

$ mvn clean install wildfly:deploy

Nach erfolgreicher Ausführung dieses Befehls im Protokoll wird Folgendes angezeigt:

[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------ 
[INFO] Total time: 8.224 s

Die erste Bereitstellung der helloworld-Anwendung auf JBoss EAP dauerte also etwas mehr als 8 Sekunden.

Wir Test Hello World

streng gemäß dem Entscheidungs Zugriff auf die Anwendungsführung , offen 127.0.0.1 : 8080 / Hello World in einem Browser und sieht folgendermaßen aus :



Feige. 2. Original Hello World von JBoss EAP.

Änderungen vornehmen

Ändern Sie den Eingabeparameter createHelloMessage (String name) von World in Marco:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Führen Sie erneut den folgenden Befehl aus:

$ mvn clean install wildfly:deploy

Dann aktualisieren wir die Seite im Browser und stellen fest, dass sich der Text geändert hat:



Feige. 3. Hallo Marco bei JBoss EAP.

Wir setzen die helloworld-Bereitstellung zurück und beenden JBoss EAP.

Dies ist optional. Wenn Sie die Bereitstellung jedoch abbrechen möchten, können Sie dies mit dem folgenden Befehl tun:

$ mvn clean install wildfly:undeploy

Um die JBoss EAP-Instanz herunterzufahren, drücken Sie einfach Strg + C im Terminalfenster.

Helloworld aufrüsten


Aktualisieren wir nun die ursprüngliche helloworld-Anwendung.

Erstellen eines neuen Zweigs

Erstellen Sie einen neuen Arbeitszweig, nachdem das Schnellstartprojekt abgeschlossen ist:

$ git checkout -b quarkus 7.2.0.GA

Ändern der Datei pom.xml

Wir werden die Anwendung von der Datei pom.xml aus ändern. Damit Quarkus XML-Blöcke einfügen kann, führen Sie den folgenden Befehl im Ordner helloworld aus:

$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create

Beim Schreiben dieses Artikels wurde Version 0.23.2 verwendet. Quarkus hat häufig neue Versionen. Welche Version die neueste ist, finden Sie unter github.com/quarkusio/quarkus/releases/latest .

Der obige Befehl fügt die folgenden Elemente in pom.xml ein:

  • Die Eigenschaft <quarkus.version>, die die zu verwendende Version von Quarkus angibt.
  • Der Block <dependencyManagement> zum Importieren der Quarkus-Stückliste (Stückliste), um nicht für jede Quarkus-Abhängigkeit eine Version hinzuzufügen.
  • Das Quarkus-Maven-Plugin-Plugin, das für das Packen der Anwendung und den Bereitstellungsmodus verantwortlich ist.
  • Ein natives Profil zum Erstellen von ausführbaren Anwendungsdateien.

Darüber hinaus nehmen wir die folgenden Änderungen manuell an pom.xml vor:

  1. <groupId> <parent> <artifactId>. <parent>, <groupId>.
  2. <parent>, Quarkus pom JBoss.
  3. <version> <artifactId>. .
  4. <packaging>, WAR, JAR.
  5. :
    1. javax.enterprise:cdi-api io.quarkus:quarkus-arc, <scope>provided</scope>, ( ) Quarkus- injection CDI.
    2. Wir ändern die Abhängigkeit org.jboss.spec.javax.servlet: jboss-servlet-api_4.0_spec in io.quarkus: quarkus-Sog, indem wir das bereitgestellte <scope> </ scope> entfernen, da diese Quarkus-Erweiterung (gemäß den Docks) Servlet-Unterstützung bietet 's.
    3. Wir entfernen die Abhängigkeit org.jboss.spec.javax.annotation: jboss-annotations-api_1.3_spec, da sie mit den gerade geänderten Abhängigkeiten geliefert wird.


Die Version der Datei pom.xml mit allen Änderungen befindet sich unter github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml .

Bitte beachten Sie, dass der obige Befehl mvn io.quarkus: quarkus-maven-plugin: 0.23.2: create nicht nur die Datei pom.xml ändert, sondern dem Projekt auch eine Reihe von Komponenten hinzufügt, nämlich die folgenden Dateien und Ordner:

  • mvnw and mvnw.cmd .mvn: Maven Wrapper Maven Maven .
  • docker ( src/main/): Dockerfile native jvm ( .dockerignore).
  • resources ( src/main/): application.properties Quarkus index.html ( . Run the modernized helloworld ).

Wir starten helloworld
Um die Anwendung zu testen, verwenden wir quarkus: dev, das Quarkus im Entwicklungsmodus startet (weitere Informationen finden Sie in diesem Abschnitt im Handbuch für den Entwicklungsmodus ).

Hinweis: Dieser Schritt wird voraussichtlich zu einem Fehler führen, da wir noch nicht alle erforderlichen Änderungen vorgenommen haben.

Führen Sie nun den Befehl aus, um zu überprüfen, wie es funktioniert:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
INFO  [org.jbo.threads] JBoss Threads version 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:841)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:214)
	at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:106)
	at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:249)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:780)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:428)
	at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:371)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:206)
	... 14 more

Also funktioniert es nicht ... Und warum?

Die Ausnahme "UnsatisfiedResolutionException" verweist auf die HelloService-Klasse, die Mitglied der HelloWorldServlet-Klasse ist (Java-Mitglied: org.jboss.as.quickstarts.helloworld.HelloWorldServlet # helloService). Das Problem ist, dass HelloWorldServlet eine injizierte Instanz von HelloService benötigt, diese jedoch nicht gefunden werden kann (obwohl sich beide Klassen im selben Paket befinden).

Es ist Zeit, zur Dokumentation zurückzukehren und zu lesen, wie Quarkus funktioniertInjizierenund daher Contexts and Dependency Injection (CDI). Aus diesem Grund öffnen wir das Handbuch zur Kontext- und Abhängigkeitsinjektion und lesen im Abschnitt Bean Discovery : „Eine Bean-Klasse ohne definierende Bean-Annotation wird nicht durchsucht.“

Wir schauen uns die HelloService-Klasse an - sie hat wirklich keine solche Anmerkung. Daher muss es hinzugefügt werden, damit Quarkus eine Bohne suchen und finden kann. Und da dies ein zustandsloses Objekt ist, können wir die Annotation @ApplicationScoped wie folgt hinzufügen:

@ApplicationScoped
public class HelloService {

Hinweis: In der Entwicklungsumgebung werden Sie möglicherweise aufgefordert, das erforderliche Paket hinzuzufügen (siehe folgende Zeile). Dies muss manuell wie folgt erfolgen:

import javax.enterprise.context.ApplicationScoped;

Wenn Sie sich nicht sicher sind, welchen Bereich Sie verwenden sollen, wenn die Quell-Bean überhaupt nicht festgelegt ist, lesen Sie die JSR 365- Dokumentation : Kontexte und Abhängigkeitsinjektion für Java 2.0 - Standardbereich .

Jetzt versuchen wir erneut, die Anwendung mit dem Befehl ./mvnw compile quarkus: dev: zu starten.

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/target/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 576ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 1.083s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Jetzt läuft alles fehlerfrei.

Wir starten die aktualisierte helloworld.
Wie im Protokoll beschrieben, öffnen Sie im Browser 0.0.0.0 : 8080 (die Standard-Quarkus-Startseite) und sehen Sie Folgendes:



Feige. 4. Startseite von Quarkus dev.

Die WebServlet-Annotation für diese Anwendung hat die folgende Kontextdefinition:

@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {

Daher gehen wir im Browser zu 0.0.0.0 : 8080 / HelloWorld und sehen Folgendes:



Feige. 5: Die Quarkus-Entwicklungsseite für die Hello World-Anwendung.

Nun, alles funktioniert.

Und jetzt nehmen wir Änderungen am Code vor. Beachten Sie, dass der Befehl ./mvnw compile quarkus: dev immer noch funktioniert und wir ihn nicht stoppen werden. Versuchen wir nun, die gleichen - trivialen - Änderungen auf den Code selbst anzuwenden und zu sehen, wie Quarkus dem Entwickler das Leben erleichtert:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Speichern Sie die Datei und aktualisieren Sie die Webseite, um Hallo Marco zu sehen, wie im folgenden Screenshot gezeigt:



Feige. 6. Hallo Marco Seite in Quarkus dev.

Überprüfen Sie nun die Ausgabe im Terminal:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Changed source files detected, recompiling [/home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/src/main/java/org/jboss/as/quickstarts/helloworld/HelloWorldServlet.java]
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus stopped in 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Quarkus augmentation completed in 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 started in 0.257s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (vert.x-worker-thread-3) Installed features: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Hot replace total time: 0.371s

Die Seitenaktualisierung löste die Erkennung von Änderungen im Quellcode aus, und Quarkus führte automatisch die Prozedur „Stop Start“ durch. Und das alles war in nur 0,371 Sekunden erledigt (hier ist es das sehr „ultraschnelle subatomare Java“).

Wir bauen helloworld in ein JAR-Paket ein. Nachdem
der Code nun ordnungsgemäß funktioniert, packen wir ihn mit dem folgenden Befehl:

$ ./mvnw clean package

Dieser Befehl erstellt zwei JAR-Dateien im Ordner / target: die Datei helloworld-.jar, ein Standardartefakt, das vom Maven-Team zusammen mit den Projektklassen und -ressourcen zusammengestellt wird. Und die helloworld-Datei ist running.jar, eine ausführbare JAR.

Beachten Sie, dass dies kein Über-JAR ist, da alle Abhängigkeiten einfach in den Ordner / target / lib kopiert (und nicht in eine JAR-Datei gepackt) werden. Um diese JAR von einem anderen Ordner oder auf einem anderen Host auszuführen, müssen Sie daher sowohl die JAR-Datei als auch den Ordner / lib dort kopieren, da das Class-Path-Element in der MANIFEST.MF-Datei im JAR-Paket eine explizite Liste der JARs enthält lib-Ordner.
Informationen zum Erstellen von Uber-JAR-Anwendungen finden Sie im Handbuch Uber-Jar-Erstellung .

Starten Sie helloworld, verpackt in einem JAR

Jetzt können Sie unsere JAR mit dem Standard-Java-Befehl ausführen:

$ java -jar ./target/helloworld-<version>-runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.673s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Nachdem dies erledigt ist, rufen Sie den Browser unter 0.0.0.0 : 8080 auf und überprüfen Sie, ob alles ordnungsgemäß funktioniert.

Helloworld in eine native ausführbare Datei

einfügen Unsere Helloworld arbeitet also als eigenständige Java-Anwendung mit Quarkus-Abhängigkeiten. Sie können jedoch noch weiter gehen und daraus eine native ausführbare Datei machen.

GraalVM installieren
Zunächst müssen Sie die erforderlichen Tools installieren:

1. Laden Sie GraalVM 19.2.0.1 von github.com/oracle/graal/releases/tag/vm-19.2.0.1 herunter .

2. Erweitern Sie das heruntergeladene Archiv:

$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz

3. Wechseln Sie in den Ordner untar.

4. Führen Sie den folgenden Befehl aus, um ein natives Image herunterzuladen und hinzuzufügen:

$ ./bin/gu install native-image

5. Wir registrieren den in Schritt 2 erstellten Ordner in der Umgebungsvariablen GRAALVM_HOME:

$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)

Weitere Informationen und Installationsanweisungen für andere Betriebssysteme finden Sie unter Erstellen einer nativen ausführbaren Datei - Voraussetzungen .

Bauen Sie helloworld in eine native ausführbare Datei ein.
Lesen Sie das Handbuch Erstellen einer nativen ausführbaren Datei - Erstellen einer nativen ausführbaren Datei : „Erstellen Sie jetzt eine native ausführbare Datei für unsere Anwendung, um die Startzeit und die Festplattengröße zu reduzieren. Die ausführbare Datei enthält alles, was zum Ausführen der Anwendung erforderlich ist, einschließlich des JVM-Computers (oder besser gesagt seiner abgeschnittenen Version, die nur das enthält, was zum Ausführen der Anwendung erforderlich ist) und unserer Anwendung selbst. “

Um eine native ausführbare Datei zu erstellen, müssen Sie das native Maven-Profil aktivieren:

$ ./mvnw package -Pnative

Unsere Montage dauerte eine Minute und 10 Sekunden, und der endgültige helloworld-Dateiläufer f wurde im Ordner / target erstellt.

Wir starten die native ausführbare Datei helloworld

Im vorherigen Schritt haben wir die ausführbare Datei / target / helloworld - running erhalten. Führen Sie es jetzt aus:

$ ./target/helloworld-<version>-runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.006s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Öffnen Sie erneut den Browser 0.0.0.0 : 8080 und überprüfen Sie, ob alles ordnungsgemäß funktioniert.

Fortsetzung folgt!

Wir glauben, dass die Methode zur Modernisierung von Java-Anwendungen unter Verwendung der in diesem Beitrag berücksichtigten Funktionen von Quarkus (wenn auch mit dem einfachsten Beispiel) im wirklichen Leben aktiv angewendet werden sollte. In diesem Fall treten wahrscheinlich eine Reihe von Problemen auf, deren Lösung wir im nächsten Beitrag teilweise betrachten werden. Diese konzentrieren sich auf die Messung des Speicherverbrauchs, um die Leistungsverbesserung zu bewerten. Dies ist ein wichtiger Teil des gesamten Anwendungsaktualisierungsprozesses.

All Articles