Exercício – Implantar um aplicativo Java EE (Jakarta EE) na JBoss EAP no Serviço de Aplicativo do Azure

Concluído

Neste exercício, você implantará um aplicativo Java EE (Jakarta EE) na JBoss EAP no Serviço de Aplicativo do Azure. Use o plug-in do Maven para configurar o projeto, compilar e implantar o aplicativo e configurar uma fonte de dados.

Configurar o aplicativo com o plug-in do Maven para o Serviço de Aplicativo do Azure

Iremos configurar nosso aplicativo executando a meta de configuração no plug-in do Maven para o Serviço de Aplicativo do Azure.

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

Importante

Se você mudou a região do servidor MySQL, também deve mudar para a mesma região do servidor do aplicativo Java EE para minimizar os atrasos de latência.
No comando, selecione Java 11 para a versão Java e JBoss EAP 7 para a pilha de execução.

Elemento de entrada Valor
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

Depois de executar o comando, você receberá mensagens como as seguintes no terminal:

$ ./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] ------------------------------------------------------------------------
$ 

Depois que o comando for concluído, você verá a seguinte entrada ser adicionada no arquivo pom.xml do Maven.

  <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

Verifique o elemento <region>. Se não for a mesma localização de instalação que a do MySQL, altere-a para a mesma localização.

Depois de adicionar a configuração acima para a implantação no Azure, adicione as seguintes entradas XML para implantar o arquivo de inicialização. O recurso <type>startup</type> implanta o script especificado como startup.sh (Linux) ou startup.cmd (Windows) em /home/site/scripts/. Configuramos o script de inicialização na etapa a seguir.

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

Observação

Você pode especificar o recurso a seguir para implantar no XML:

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

    • type=war implantará o arquivo war em /home/site/wwwroot/app.war se path não for especificado
    • type=war&path=webapps/<appname>\ se comportará exatamente como a implantação war descompactando o aplicativo para /home/site/wwwroot/webapps/<appname>
    • type=jar implantará o arquivo war em /home/site/wwwroot/app.jar. O parâmetro path será ignorado
    • type=ear implantará o arquivo war em /home/site/wwwroot/app.ear. O parâmetro path será ignorado
    • type=lib implantará jar em /home/site/libs. O parâmetro path deve ser especificado
    • type=static implantará o script em /home/site/scripts. O parâmetro path deve ser especificado
    • type=startup implantará o script como startup.sh (Linux) ou startup.cmd (Windows) em /home/site/scripts/. O parâmetro path será ignorado
    • type=zip descompactará zip em /home/site/wwwroot. O parâmetro path é opcional.

Agora, verifique os valores do nome do grupo de recursos e do nome do aplicativo no arquivo XML acima. Anote esses nomes ou atribua-os a variáveis de ambiente.

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

Se você está usando Bash, configure as variáveis de ambiente com o comando a seguir. Você usará esses valores mais tarde.

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

Compilar e criar o aplicativo Java EE

Depois de definir as configurações de implantação do Serviço de Aplicativo do Azure, compile e empacote o código-fonte.

./mvnw clean package

A seguinte saída é exibida no terminal:

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

Implantar o aplicativo Java EE na JBoss EAP no Serviço de Aplicativo do Azure

Depois de compilar e empacotar o código, implante o aplicativo:

./mvnw azure-webapp:deploy

A seguinte mensagem é exibida no terminal:

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

Anote a URL do aplicativo implantado, especialmente a seguinte linha na saída do Maven:

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

Configurar uma conexão de banco de dados

O aplicativo de exemplo se conecta ao Banco de Dados MySQL e exibe os dados.

Na configuração do projeto do Maven no pom.xml, especificamos o driver JDBC do MySQL da seguinte maneira:

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

Como resultado, a JBoss EAP instala automaticamente o driver JDBC em seu pacote de implantação (ROOT.war). Você pode consultar o nome do driver JDBC do MySQL da seguinte maneira:

ROOT.war_com.mysql.cj.jdbc.Driver_8_0

Criar um objeto DataSource MySQL na JBoss EAP

Para acessar o Banco de Dados do Azure para MySQL, você precisa configurar o objeto DataSource na JBoss EAP e especificar o nome da JNDI em seu código-fonte.

Para criar um objeto DataSource do MySQL na JBoss EAP, criamos o script do shell de inicialização a seguir. O arquivo de script é createMySQLDataSource.sh no diretório /WEB-INF.

Observação

No script, associamos o DataSource MySQL usando um comando da CLI do JBoss. A cadeia de conexão, o nome de usuário e a senha usam as variáveis de ambiente MYSQL_CONNECTION_URL, MYSQL_USER e MYSQL_PASSWORD.

A origem do arquivo de script é mostrada em seguida. Esse arquivo de script já foi carregado no Serviço de Aplicativo, mas ainda não foi configurado para ser invocado.

#!/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

Agora, configure sua instância do Serviço de Aplicativo para invocar o script de inicialização:

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

Após a execução do script, ele será invocado toda vez que o servidor de aplicativos for reiniciado.

Observação

Se o seu artefato de implantação não for ROOT.war, você precisará alterar o valor de --driver-name=YOUR_ARTIFACT.war_com.mysql.cj.jdbc.Driver_8_0 também.

Configurar as variáveis de ambiente para conexão ao MySQL

Depois de configurar o script de inicialização, configure o Serviço de Aplicativo para usar determinadas variáveis de 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

Dica

Os valores de MYSQL_CONNECTION_URL, MYSQL_USER e MYSQL_PASSWORD foram definidos usando a unidade anterior.

Confirmar a referência de DataSource no código

Para acessar o banco de dados MySQL do seu aplicativo, você precisa configurar a referência de fonte de dados em seu projeto de aplicativo. Implementamos o código de acesso de banco de dados usando a JPA (API de Persistência Java).

A configuração para a referência de DataSource foi adicionada no persistence.xml, que é o arquivo de configuração do JPA.

Acesse o seguinte arquivo:

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

Verifique se o nome do DataSource corresponde ao nome usado na configuração. O código já criou o nome JNDI como 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>

Em seguida, você pode acessar o banco de dados MySQL referenciado no nome da unidade PersistenceContext da seguinte maneira:

@Transactional(REQUIRED)
@RequestScoped
public class CityService {

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

Acessar o aplicativo

No aplicativo de exemplo, implementamos três pontos de extremidade REST. Você pode acessar o aplicativo e validar esses pontos de extremidade usando um navegador da Web ou um comando curl.

Para acessar o aplicativo, você precisa fazer referência à URL do aplicativo, que você obteve em uma seção anterior:

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

Execute o comando a seguir para obter todas as informações de continente no 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"]$ 

Se você especificar o continente na URL, poderá obter todos os países/todas as regiões do continente especificado.

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

Por fim, se você especificar um código de país/região após /countries, poderá obter todas as cidades com uma população com mais um milhão dentro do país/da região.

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"

Resumo do exercício

Você validou os pontos de extremidade REST do aplicativo e testou se seu aplicativo pode obter dados do banco de dados MySQL.

Na próxima unidade, você examinará os logs do servidor.