Esercizio: Distribuire un'applicazione Jakarta EE in JBoss EAP nel servizio app di Azure

Completato

In questa unità si distribuisce un'applicazione Jakarta EE in Red Hat JBoss Enterprise Application Platform (JBoss EAP) nel servizio app di Azure. Usa il Maven Plugin per Azure App Service per configurare il progetto, compilare e distribuire l'applicazione, e configurare una fonte di dati.

Configurare l'app

Configurare l'app con il plug-in Maven per il servizio app di Azure seguendo questa procedura:

  1. Eseguire l'obiettivo di configurazione del plug-in di Azure in modo interattivo usando il comando seguente:

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

    Importante

    Se si modifica l'area del server MySQL, è necessario associare tale area all'area del server applicazioni Jakarta EE per ridurre al minimo i ritardi di latenza.

  2. Usare i valori nella tabella seguente per rispondere alle richieste interattive:

    Elemento di input Valore
    Create new run configuration (Y/N) [Y]: Y
    Define value for OS [Linux]: Linux
    Define value for javaVersion [Java 17]: 1: Java 17
    Define value for runtimeStack: 3: Jbosseap 7
    Define value for pricingTier [P1v3]: P1v3
    Confirm (Y/N) [Y]: Y

    L'output seguente è tipico:

    [INFO] Saving configuration to pom.
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  03:00 min
    [INFO] Finished at: 2025-02-21T06:24:11+09:00
    [INFO] ------------------------------------------------------------------------
    

    Dopo aver usato il comando Maven, il seguente esempio è un'aggiunta tipica al file Maven pom.xml:

    <build>
      <finalName>ROOT</finalName>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.4.0</version>
        </plugin>
        <plugin>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-webapp-maven-plugin</artifactId>
            <version>2.13.0</version>
            <configuration>
                <schemaVersion>v2</schemaVersion>
                <resourceGroup>jakartaee-app-on-jboss-rg</resourceGroup>
                <appName>jakartaee-app-on-jboss</appName>
                <pricingTier>P1v3</pricingTier>
                <region>centralus</region>
                <runtime>
                    <os>Linux</os>
                    <javaVersion>Java 17</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>
    
  3. Controllare l'elemento <region> nel file pom.xml . Se il valore non corrisponde al percorso di installazione di MySQL, modificarlo nello stesso percorso.

  4. Usare l'esempio seguente per modificare il valore webContainer nel file pom.xml in Jbosseap 8 per l'ambiente JBoss EAP 8 in Servizio app di Azure:

    Suggerimento

    A partire da febbraio 2025, la versione più recente disponibile di JBoss EAP è la 8.0 Update 4.1.

    <runtime>
        <os>Linux</os>
        <javaVersion>Java 17</javaVersion>
        <webContainer>Jbosseap 8</webContainer> <!-- Change this value -->
    </runtime>
    
  5. Aggiungere il codice XML seguente all'elemento <resources> del file pom.xml . Questa configurazione viene usata per distribuire il file di avvio, che verrà aggiornato più avanti in questa unità.

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

    Il valore della risorsa <type> di startup distribuisce lo script specificato come file di startup.sh per Linux o startup.cmd per Windows. Il percorso di distribuzione è /home/site/scripts/.

    Annotazioni

    È possibile scegliere l'opzione di distribuzione e il percorso di distribuzione specificando type in uno dei modi seguenti:

    • type=war distribuisce il file WAR in /home/site/wwwroot/app.war se path non è specificato.
    • type=war&path=webapps/<appname> distribuisce il file WAR in /home/site/wwwroot/webapps/<appname>.
    • type=jar distribuisce il file WAR in /home/site/wwwroot/app.jar. Il parametro path viene ignorato.
    • type=ear distribuisce il file WAR in /home/site/wwwroot/app.ear. Il parametro path viene ignorato.
    • type=lib distribuisce il file JAR in /home/site/libs. È necessario specificare il path parametro .
    • type=static distribuisce lo script in /home/site/scripts. Specificare il parametro path.
    • type=startup distribuisce lo script come startup.sh in Linux o startup.cmd in Windows. Lo script viene distribuito in /home/site/scripts/. Il parametro path viene ignorato.
    • type=zip decomprime il file .zip in /home/site/wwwroot. Il path parametro è facoltativo.
  6. Controllare i valori degli elementi resourceGroup e appName nel file pom.xml.

  7. Assegnare i valori per resourceGroup e appName alle variabili di ambiente usando i comandi seguenti:

    export RESOURCE_GROUP_NAME=<resource-group>
    export WEB_APP_NAME=<app-name>
    

Compilare e generare l'app Jakarta EE

Dopo aver configurato le impostazioni di distribuzione del servizio app di Azure, compilare e creare il pacchetto del codice sorgente usando il comando seguente:

./mvnw clean package

L'output seguente è tipico:

[INFO] --- war:3.4.0:war (default-war) @ jakartaee-app-on-jboss ---
[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] Building war: /private/tmp/mslearn-jakarta-ee-azure/target/ROOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.881 s
[INFO] Finished at: 2025-02-21T06:32:30+09:00
[INFO] ------------------------------------------------------------------------

Distribuire l'app Jakarta EE su JBoss EAP su Azure App Service

Dopo la compilazione e il pacchetto del codice, distribuire l'applicazione usando il comando seguente:

./mvnw azure-webapp:deploy

Verrà visualizzato l'output che include un messaggio di operazione riuscita e l'URL dell'applicazione distribuita. Assicurarsi di salvare l'URL per usarlo in un secondo momento.

Configurare una connessione di database

L'applicazione di esempio si connette al database MySQL e visualizza i dati. La configurazione del progetto Maven nel file pom.xml specifica il driver JDBC MySQL, come illustrato nell'esempio seguente:

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

Di conseguenza, JBoss EAP installa automaticamente il driver ROOT.war_com.mysql.cj.jdbc.Driver_9_2 JDBC nel pacchetto di distribuzione ROOT.war.

Creare l'oggetto MySQL DataSource in JBoss EAP

Per accedere a Database di Azure per MySQL, è necessario configurare l'oggetto DataSource in JBoss EAP e specificare il nome Java Naming and Directory Interface (JNDI) nel codice sorgente. Per creare un oggetto MySQL DataSource in JBoss EAP, usare lo script della shell di avvio /WEB-INF/createMySQLDataSource.sh . L'esempio seguente mostra una versione non configurata dello script già nel servizio app di Azure:

#!/bin/bash
# In order to use the variables in 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=${AZURE_MYSQL_CONNECTIONSTRING}&characterEncoding=utf8&sslMode=REQUIRED&serverTimezone=UTC&authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin \
--driver-name=ROOT.war_com.mysql.cj.jdbc.Driver_9_2 \
--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

Annotazioni

Quando si crea l'origine dati, non si specifica una password per la connessione MySQL. La variabile AZURE_MYSQL_CONNECTIONSTRING di ambiente viene specificata nel --connection-url parametro . Questa variabile di ambiente viene impostata automaticamente quando viene creata la connessione al servizio in un secondo momento.

Il valore di connessione del servizio è impostato su jdbc:mysql://$MYSQL_SERVER_INSTANCE.mysql.database.azure.com:3306/world?serverTimezone=UTC&sslmode=required&user=aad_jbossapp, che usa il aad_jbossapp nome utente senza password. Aggiungendo &authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin a questo URL, l'autenticazione ID Entra di Microsoft è abilitata per l'utente aad_jbossapp.

Configurare l'istanza del servizio app per richiamare lo script di avvio usando il comando seguente:

az webapp config set \
    --resource-group ${RESOURCE_GROUP_NAME} \
    --name ${WEB_APP_NAME} \
    --startup-file '/home/site/scripts/startup.sh'

Dopo l'esecuzione dello script, il server applicazioni lo richiama ogni volta che il server applicazioni viene riavviato.

Annotazioni

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

Configurare la connessione al servizio per il server flessibile MySQL

Dopo aver configurato lo script di avvio, configurare il servizio app per l'uso di Service Connector per la connessione al server flessibile MySQL attenendosi alla procedura seguente:

  1. Impostare le variabili di ambiente usando i comandi seguenti:

    export PASSWORDLESS_USER_NAME_SUFFIX=jbossapp
    export SOURCE_WEB_APP_ID=$(az webapp list \
        --resource-group  $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    export MYSQL_ID=$(az mysql flexible-server list \
        --resource-group $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    export TARGET_MYSQL_ID=$MYSQL_ID/databases/world
    export MANAGED_ID=$(az identity list \
        --resource-group $RESOURCE_GROUP_NAME \
        --query "[0].id" \
        --output tsv)
    

    Le variabili di ambiente vengono usate per gli scopi seguenti:

    • PASSWORDLESS_USER_NAME_SUFFIX è il suffisso per il nome utente usato per connettersi al server flessibile MySQL. Il nome utente creato ha il prefisso aad_ seguito dal suffisso specificato.
    • SOURCE_WEB_APP_ID è l'ID dell'istanza del servizio app di Azure usata per connettersi al server flessibile MySQL.
    • MYSQL_ID è l'ID del server flessibile MySQL.
    • TARGET_MYSQL_ID specifica il nome del database $MYSQL_ID/databases/world per stabilire una connessione con un utente autorizzato ad accedere al database world.
    • MANAGED_ID è l'identità gestita usata per connettersi al server flessibile MySQL.
  2. Aggiungere l'estensione per serviceconnector-passwordless e creare la connessione al servizio usando i comandi seguenti:

    az extension add \
        --name serviceconnector-passwordless \
        --upgrade
    az webapp connection create mysql-flexible \
        --resource-group ${RESOURCE_GROUP_NAME} \
        --connection $PASSWORDLESS_USER_NAME_SUFFIX \
        --source-id $SOURCE_WEB_APP_ID \
        --target-id $TARGET_MYSQL_ID \
        --client-type java \
        --system-identity mysql-identity-id=$MANAGED_ID
    

    Annotazioni

    se viene visualizzato un messaggio di errore come Resource '********-****-****-****-************' does not exist or one of its queried reference-property objects are not present., eseguire di nuovo il comando dopo alcuni secondi.

  3. Al prompt di SQL controllare l'elenco degli utenti registrati in MySQL usando la query seguente:

    SELECT user, host, plugin FROM mysql.user;
    

    L'output seguente è tipico:

    +----------------------------------+-----------+-----------------------+
    | user                             | host      | plugin                |
    +----------------------------------+-----------+-----------------------+
    | aad_jbossapp                     | %         | aad_auth              |
    | azureuser                        | %         | mysql_native_password |
    | $CURRENT_AZ_LOGIN_USER_NAME#EXT#@| %         | aad_auth              |
    | azure_superuser                  | 127.0.0.1 | mysql_native_password |
    | azure_superuser                  | localhost | mysql_native_password |
    | mysql.infoschema                 | localhost | caching_sha2_password |
    | mysql.session                    | localhost | caching_sha2_password |
    | mysql.sys                        | localhost | caching_sha2_password |
    +----------------------------------+-----------+-----------------------+
    8 rows in set (2.06 sec)
    

    Verrà visualizzato un aad_jbossapp utente che usa il aad_auth plug-in. Da JBoss EAP distribuito in Azure è possibile connettersi al server flessibile MySQL usando il aad_jbossapp nome utente senza password.

Verificare il riferimento a DataSource nel codice

Per accedere al database MySQL dall'applicazione, è necessario configurare il riferimento all'origine dati nel progetto dell'applicazione.

Il codice di accesso al database viene implementato usando l'API Java Persistence (JPA). La configurazione per il DataSource riferimento si trova nel file di configurazione JPA persistence.xml.

Per confermare il DataSource riferimento, seguire questa procedura:

  1. Aprire il file src/main/resources/META-INF/persistence.xml e verificare se il DataSource nome corrisponde al nome usato nella configurazione. Lo script di avvio ha già creato il nome JNDI come java:jboss/datasources/JPAWorldDataSource, come illustrato nell'esempio seguente:

    <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>
    
  2. Accedere al database MySQL nel nome dell'unità PersistenceContext , come illustrato nell'esempio seguente:

    @Transactional(REQUIRED)
    @RequestScoped
    public class CityService {
    
        @PersistenceContext(unitName = "JPAWorldDatasourcePU")
        EntityManager em;
    

Accedere all'applicazione

L'applicazione di esempio implementa tre endpoint REST. Per accedere all'applicazione e recuperare i dati, seguire questa procedura:

  1. Usare il browser per passare all'URL dell'applicazione, visualizzato nell'output quando è stata distribuita l'applicazione.

  2. Per ottenere tutte le informazioni sui continenti in formato JSON, usa il metodo GET sull'endpoint area.

    Screenshot dell'endpoint area.

  3. Per ottenere tutti i paesi e le aree geografiche in un continente specificato, usare il GET metodo nell'endpoint area e specificare un continent parametro path.

    Screenshot dell'endpoint area con un parametro di percorso continente.

  4. Per ottenere tutte le città con una popolazione maggiore di un milione all'interno del paese o dell'area geografica specificata, usare il GET metodo nell'endpoint countries e specificare un parametro di countrycode percorso.

    Screenshot dell'endpoint paesi con il parametro di percorso countrycode.

Esercizio di riepilogo

In questa unità sono stati convalidati gli endpoint REST dell'applicazione e si è verificato che l'applicazione può ottenere dati dal database MySQL. Nell'unità successiva si esaminano i log del server.