Esercizio - Distribuire un'applicazione Java EE (Jakarta EE) in JBoss EAP nel servizio app di Azure

Completato

In questo esercizio si distribuirà un'applicazione Java EE (Jakarta EE) in JBoss EAP nel servizio app di Azure. Si userà il plug-in Maven per configurare il progetto, compilare e distribuire l'applicazione, nonché configurare un'origine dati.

Configurare l'app con il plug-in Maven per il servizio app di Azure

Configurare l'applicazione eseguendo l'obiettivo di configurazione nel plug-in Maven per il Servizio app di Azure.

./mvnw com.microsoft.azure:azure-webapp-maven-plugin:2.9.0:config

Importante

Se è stata modificata l'area del server MySQL, è necessario anche scegliere la stessa area per il server applicazione Java EE in modo da ridurre al minimo i ritardi di latenza.
Nel comando selezionare Java 11 come versione Java e JBoss EAP 7 come stack di runtime.

Elemento di input Valore
Available subscriptions: Your appropriate subsctioption
Choose a Web Container Web App [\<create\>]: 1: <create>
Define value for OS [Linux]: Linux
Define value for javaVersion [Java 17]: 2: Java 11
Define value for runtimeStack: 1: Jbosseap 7
Define value for pricingTier [P1v3]: P1v3
Confirm (Y/N) [Y]: Y

Dopo avere eseguito il comando, nel terminale verranno visualizzati messaggi come il seguente:

$ ./mvnw com.microsoft.azure:azure-webapp-maven-plugin:2.9.0:config
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------< com.microsoft.azure.samples:jakartaee-app-on-jboss >---------
[INFO] Building jakartaee-app-on-jboss 1.0-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO] 
[INFO] --- azure-webapp-maven-plugin:2.5.0:config (default-cli) @ jakartaee-app-on-jboss ---
[WARNING] The POM for com.microsoft.azure.applicationinsights.v2015_05_01:azure-mgmt-insights:jar:1.0.0-beta is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[INFO] Auth type: OAUTH2
Username: YOUR_EMAIL_ADDRESS@microsoft.com
Available subscriptions:
[INFO] Subscription: YOUR_SUBSCRIPTION(********-****-****-****-************)
[INFO] It may take a few minutes to load all Java Web Apps, please be patient.
Web Container Web Apps in subscription Microsoft Azure Internal Billing-CDA:
* 1: <create>
  2: jakartaee-app-on-jboss-yoshio (linux, jbosseap 7.2-java8)
Please choose a Web Container Web App [<create>]: 
Define value for OS [Linux]:
* 1: Linux
  2: Windows
  3: Docker
Enter your choice: 
Define value for javaVersion [Java 8]:
* 1: Java 8
  2: Java 11
Enter your choice: 
Define value for runtimeStack:
  1: Jbosseap 7.2
  2: Jbosseap 7
* 3: Tomcat 8.5
  4: Tomcat 9.0
Enter your choice: 1
Define value for pricingTier [P1v3]:
  1: P3v3
  2: P2v3
* 3: P1v3
Enter your choice: 
Please confirm webapp properties
Subscription Id : ********-****-****-****-************
AppName : jakartaee-app-on-jboss-1625038814881
ResourceGroup : jakartaee-app-on-jboss-1625038814881-rg
Region : westeurope
PricingTier : P1v3
OS : Linux
Java : Java 8
Web server stack: Jbosseap 7.2
Deploy to slot : false
Confirm (Y/N) [Y]: 
[INFO] Saving configuration to pom.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:43 min
[INFO] Finished at: 2021-06-30T16:40:47+09:00
[INFO] ------------------------------------------------------------------------
$ 

Al termine del comando si noterà che nel file Maven pom.xml è stata aggiunta la voce seguente.

  <build>
    <finalName>ROOT</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.3.2</version>
      </plugin>
        <plugin>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-webapp-maven-plugin</artifactId>
            <version>2.9.0</version>
            <configuration>
                <schemaVersion>v2</schemaVersion>
                <resourceGroup>jakartaee-app-on-jboss-1625038814881-rg</resourceGroup>
                <appName>jakartaee-app-on-jboss-1625038814881</appName>
                <pricingTier>P1v3</pricingTier>
                <region>centralus</region>
                <runtime>
                    <os>Linux</os>
                    <javaVersion>Java 11</javaVersion>
                    <webContainer>Jbosseap 7</webContainer>
                </runtime>
                <deployment>
                    <resources>
                        <resource>
                            <directory>${project.basedir}/target</directory>
                            <includes>
                                <include>*.war</include>
                            </includes>
                        </resource>
                    </resources>
                </deployment>
            </configuration>
        </plugin>
    </plugins>
  </build>

Importante

Controllare l'elemento <region>. Se non corrisponde alla posizione di installazione di MySQL, modificarlo scegliendo la stessa posizione.

Dopo aver aggiunto la configurazione precedente per la distribuzione in Azure, aggiungere le voci XML seguenti per distribuire il file di avvio. La risorsa <type>startup</type> distribuisce lo script specificato come startup.sh (Linux) o startup.cmd (Windows) in /home/site/scripts/. Lo script di avvio verrà configurato nel passaggio successivo.

              <!-- Please add following lines -->
              <resource>
                <type>startup</type>
                <directory>${project.basedir}/src/main/webapp/WEB-INF/</directory>
                <includes>
                  <include>createMySQLDataSource.sh</include>
                </includes>
              </resource>
              <!-- Please add following lines -->

Nota

È possibile specificare la risorsa seguente per la distribuzione in XML:

  • type=<war|jar|ear|lib|startup|static|zip>

    • type=war distribuirà il file con estensione war in /home/site/wwwroot/app.war se pathnon è specificato
    • type=war&path=webapps/<appname>\ avrà esattamente lo stesso comportamento di wardeploy decomprimendo l'app in /home/site/wwwroot/webapps/<appname>
    • type=jar distribuirà il file con estensione war in /home/site/wwwroot/app.jar. Il parametro path verrà ignorato
    • type=ear distribuirà il file con estensione war in /home/site/wwwroot/app.ear. Il parametro path verrà ignorato
    • type=lib distribuirà il file con estensione jar in /home/site/libs. È necessario specificare il parametro path
    • type=static distribuirà lo script in /home/site/scripts. È necessario specificare il parametro path
    • type=startup distribuirà lo script come startup.sh (Linux) o startup.cmd (Windows) in /home/site/scripts/. Il parametro path verrà ignorato
    • type=zip decomprimerà il file con estensione zip in /home/site/wwwroot. Il parametro path è facoltativo.

Controllare ora i valori per il nome del gruppo di risorse e il nome dell'applicazione dal file XML precedente. Prendere nota di questi nomi o, ancora meglio, assegnarli alle variabili di ambiente.

<resourceGroup>jakartaee-app-on-jboss-1625038814881-rg</resourceGroup>
<appName>jakartaee-app-on-jboss-1625038814881</appName>

Se viene usato Bash, configurare le variabili di ambiente con il comando seguente. Questi valori verranno utilizzati più avanti.

export RESOURCEGROUP_NAME=jakartaee-app-on-jboss-1625038814881-rg
export WEBAPP_NAME=jakartaee-app-on-jboss-1625038814881

Compilare e creare l'app Java EE

Dopo aver configurato le impostazioni di distribuzione del servizio app di Azure, compilare il codice sorgente e creare il relativo pacchetto.

./mvnw clean package

Nel terminale verrà visualizzato l'output seguente:

[INFO] Packaging webapp
[INFO] Assembling webapp [jakartaee-app-on-jboss] in [/private/tmp/mslearn-jakarta-ee-azure/target/ROOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/private/tmp/mslearn-jakarta-ee-azure/src/main/webapp]
[INFO] Webapp assembled in [369 msecs]
[INFO] Building war: /private/tmp/mslearn-jakarta-ee-azure/target/ROOT.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.656 s
[INFO] Finished at: 2023-03-04T12:35:43-05:00
[INFO] ------------------------------------------------------------------------

Distribuire l'app Java EE in JBoss EAP nel servizio app di Azure

Dopo aver compilato il codice e aver creato il relativo pacchetto, distribuire l'applicazione:

./mvnw azure-webapp:deploy

Nel terminale verrà visualizzato il messaggio seguente:

[INFO] Creating resource group jakartaee-app-on-jboss-1625038814881-rg in region westeurope...
[INFO] Successfully created resource group jakartaee-app-on-jboss-1625038814881-rg.
[INFO] Creating app service plan...
[INFO] Successfully created app service plan asp-jakartaee-app-on-jboss-1625038814881.
[INFO] Creating web app jakartaee-app-on-jboss-1625038814881...
[INFO] Successfully created Web App jakartaee-app-on-jboss-1625038814881.
[INFO] Trying to deploy artifact to jakartaee-app-on-jboss-1625038814881...
[INFO] Deploying (/private/tmp/mslearn-jakarta-ee-azure/target/ROOT.war)[war]  ...
[INFO] Successfully deployed the artifact to https://jakartaee-app-on-jboss-1625038814881.azurewebsites.net
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:11 min
[INFO] Finished at: 2023-03-04T12:38:39-05:00
[INFO] ------------------------------------------------------------------------

Prendere nota dell'URL dell'applicazione distribuita, in particolare della riga seguente nell'output di Maven:

[INFO] Successfully deployed the artifact to https://jakartaee-app-on-jboss-1625038814881.azurewebsites.net

Configurare una connessione di database

L'applicazione di esempio si connette al database MySQL e mostra i dati.

Nella configurazione del progetto Maven in pom.xml è stato specificato il driver JDBC per MySQL come illustrato di seguito:

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql-jdbc-driver}</version>
    </dependency>

Di conseguenza, JBoss EAP installa automaticamente il driver JDBC nel pacchetto di distribuzione (ROOT.war). È possibile fare riferimento al nome del driver JDBC per MySQL come illustrato di seguito:

ROOT.war_com.mysql.cj.jdbc.Driver_8_0

Creare l'oggetto DataSource MySQL in JBoss EAP

Per accedere a Database di Azure per MySQL, è necessario configurare l'oggetto DataSource in JBoss EAP e specificare il nome JNDI nel codice sorgente.

Per creare un oggetto DataSource MySQL in JBoss EAP, è stato creato lo script della shell di avvio seguente. Il file di script è createMySQLDataSource.sh nella directory /WEB-INF.

Nota

Nello script viene eseguito il binding dell'oggetto DataSource MySQL usando un comando dell'interfaccia della riga di comando di JBoss. Per la stringa di connessione, il nome utente e la password vengono usate le variabili di ambiente MYSQL_CONNECTION_URL, MYSQL_USER e MYSQL_PASSWORD.

L'origine del file di script viene mostrata di seguito. Questo file di script è già stato caricato in servizio app, ma non è ancora stato configurato per essere richiamato.

#!/usr/bin/bash

# In order to use the variables in JBoss CLI scripts
# https://access.redhat.com/solutions/321513
#
sed -i -e "s|.*<resolve-parameter-values.*|<resolve-parameter-values>true</resolve-parameter-values>|g" /opt/eap/bin/jboss-cli.xml

/opt/eap/bin/jboss-cli.sh --connect <<EOF
data-source add --name=JPAWorldDataSourceDS \
--jndi-name=java:jboss/datasources/JPAWorldDataSource \
--connection-url=${MYSQL_CONNECTION_URL} \
--driver-name=ROOT.war_com.mysql.cj.jdbc.Driver_8_0 \
--user-name=${MYSQL_USER} \
--password=${MYSQL_PASSWORD} \
--min-pool-size=5 \
--max-pool-size=20 \
--blocking-timeout-wait-millis=5000 \
--enabled=true \
--driver-class=com.mysql.cj.jdbc.Driver \
--jta=true \
--use-java-context=true \
--valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker \
--exception-sorter-class-name=com.mysql.cj.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
exit
EOF

Ora configurare l'istanza del servizio app per richiamare lo script di avvio:

az webapp config set --startup-file '/home/site/scripts/startup.sh' \
-n ${WEBAPP_NAME} \
-g ${RESOURCEGROUP_NAME}

Dopo l'esecuzione, lo script verrà richiamato ogni volta che il server applicazioni viene riavviato.

Nota

Se l'artefatto di distribuzione non è ROOT.war, è necessario modificare anche il valore --driver-name=YOUR_ARTIFACT.war_com.mysql.cj.jdbc.Driver_8_0.

Configurare le variabili di ambiente per la connessione a MySQL

Dopo aver configurato lo script di avvio, configurare il servizio app per l'uso di determinate variabili di ambiente:

az webapp config appsettings set \
  --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} \
  --settings \
  MYSQL_CONNECTION_URL='jdbc:mysql://mysqlserver-**********.mysql.database.azure.com:3306/world?useSSL=true&requireSSL=false' \
  MYSQL_PASSWORD='************' \
  MYSQL_USER=azureuser

Suggerimento

I valori di MYSQL_CONNECTION_URL, MYSQL_USER e MYSQL_PASSWORD sono stati impostati nell'unità precedente.

Verificare il riferimento a DataSource nel codice

Per accedere al database MySQL dall'applicazione, è necessario configurare il riferimento all'origine dati nel progetto di applicazione. Il codice di accesso al database è stato implementato usando JPA (Java Persistence API).

La configurazione per il riferimento a DataSource è stata aggiunta in persistence.xml, ovvero il file di configurazione di JPA.

Accedere al file seguente:

├── src
│   ├── main
│   │   ├── resources
│   │   │   └── META-INF
│   │   │       └── persistence.xml

Controllare se il nome di DataSource corrisponde al nome usato nella configurazione. Il codice ha già creato il nome JNDI come java:jboss/datasources/JPAWorldDataSource:

  <persistence-unit name="JPAWorldDatasourcePU" transaction-type="JTA">
    <jta-data-source>java:jboss/datasources/JPAWorldDataSource</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.generate_statistics" value="true" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
  </persistence-unit>
</persistence>

Sarà quindi possibile accedere al database MySQL a cui viene fatto riferimento nel nome di unità PersistenceContext come indicato di seguito:

@Transactional(REQUIRED)
@RequestScoped
public class CityService {

    @PersistenceContext(unitName = "JPAWorldDatasourcePU")
    EntityManager em;

Accedere all'applicazione

Nell'applicazione di esempio sono stati implementati tre endpoint REST. È possibile accedere all'applicazione e convalidare questi endpoint usando un Web browser o un comando curl.

Per accedere all'applicazione, è necessario fare riferimento all'URL dell'applicazione stessa, ottenuto in una sezione precedente:

[INFO] Successfully deployed the artifact to  
https://jakartaee-app-on-jboss-1606464084546.azurewebsites.net

Eseguire il comando seguente per ottenere le informazioni sui continenti in formato JSON.

Screenshot that shows area as the REST endpoint.

$ curl https://${WEBAPP_NAME}.azurewebsites.net/area
["North America","Asia","Africa","Europe","South America","Oceania","Antarctica"]$ 

Specificando il continente nell'URL, è possibile ottenere tutti i paesi/aree geografiche del continente specificato.

Screenshot that shows continent as the REST endpoint.

$ curl https://${WEBAPP_NAME}.azurewebsites.net/area/Asia | jq '.[] | { name: .name, code: .code }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--   100 16189  100 16189    0     0  65278      0 --:--:-- --:--:-- --:--:-- 65542
{
  "name": "Afghanistan",
  "code": "AFG"
}
{
  "name": "United Arab Emirates",
  "code": "ARE"
}
{
  "name": "Armenia",
  "code": "ARM"
}
{
  "name": "Azerbaijan",
  "code": "AZE"
}
{
  "name": "Bangladesh",
  "code": "BGD"
}
....

Infine, specificando un codice paese/area geografica dopo /countries, è possibile ottenere tutte le città in tale paese/area geografica con una popolazione superiore a 1 milione.

Screenshot that shows cities as the REST endpoint.

$ curl https://${WEBAPP_NAME}.azurewebsites.net/countries/JPN | jq '.[].name'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--   100   788  100   788    0     0   2671      0 --:--:-- --:--:-- --:--:--  2662
"Tokyo"
"Jokohama [Yokohama]"
"Osaka"
"Nagoya"
"Sapporo"
"Kioto"
"Kobe"
"Fukuoka"
"Kawasaki"
"Hiroshima"
"Kitakyushu"

Esercizio di riepilogo

A questo punto sono stati convalidati gli endpoint REST dell'applicazione e si è verificato che l'applicazione possa ottenere i dati dal database MySQL.

Nell'unità successiva verranno esaminati i log del server.