练习 - 将 Java EE (Jakarta EE) 应用程序部署到 Azure 应用服务上的 JBoss EAP

已完成

在此练习中,你需要将 Java EE (Jakarta EE) 应用程序部署到 Azure 应用服务上的 JBoss EAP。 使用 Maven 插件配置项目,编译和部署应用程序以及配置数据源。

使用 Azure 应用服务的 Maven 插件配置应用

通过在用于 Azure 应用服务的 Maven 插件中执行配置目标来配置应用程序。

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

重要

如果更改了 MySQL 服务器的区域,则也应将 Java EE 应用程序服务器的区域更改为同一区域,以最大程度地减小延迟时间。
在该命令中,为 Java 版本选择 Java 11,为运行时堆栈选择 JBoss EAP 7。

输入元素 “值”
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

运行此命令后,终端中会显示如下消息:

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

命令完成后,可看到 Maven pom.xml 文件中添加了以下条目。

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

重要

检查 <region> 元素。 如果它与 MySQL 的安装位置不同,请将其更改为同一位置。

添加上述配置以部署到 Azure 后,添加以下 XML 条目以部署启动文件。 资源 <type>startup</type> 将指定脚本作为 startup.sh (Linux) 或 startup.cmd (Windows) 部署到 /home/site/scripts/。 在下一步配置启动脚本。

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

备注

可在 XML 中指定下列要部署的资源:

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

    • 如果未指定 pathtype=war 会将 WAR 文件部署到 /home/site/wwwroot/app.war
    • type=war&path=webapps/<appname>\ 会像 wardeploy 一样,将应用解压缩到 /home/site/wwwroot/webapps/<appname>
    • type=jar 会将 WAR 文件部署到 /home/site/wwwroot/app.jar。 将忽略 path 参数
    • type=ear 会将 WAR 文件部署到 /home/site/wwwroot/app.ear。 将忽略 path 参数
    • type=lib 会将 JAR 部署到 /home/site/libs。 必须指定 path 参数
    • type=static 会将脚本部署到 /home/site/scripts。 必须指定 path 参数
    • type=startup 将脚本作为 startup.sh (Linux) 或 startup.cmd (Windows) 部署到 /home/site/scripts/。 将忽略 path 参数
    • type=zip Zip 文件解压缩到 /home/site/wwwrootpath 参数是可选的。

现在,请检查上述 XML 文件中资源组名称和应用程序名称的值。 请注意这些名称,或将其分配给环境变量。

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

如果使用的是 Bash,请使用以下命令配置环境变量。 以后会使用这些值。

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

编译并生成 Java EE 应用

配置 Azure 应用服务部署设置后,编译并打包源代码。

./mvnw clean package

终端中会显示以下输出:

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

将 Java EE 应用部署到 Azure 应用服务上的 JBoss EAP

编译并打包代码后,部署应用程序:

./mvnw azure-webapp:deploy

终端中会显示以下消息:

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

记下已部署的应用程序的 URL,尤其是 Maven 输出中的以下行:

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

配置数据库连接

示例应用程序会连接到你的 MySQL 数据库并显示数据。

pom.xml 的 Maven 项目配置中,我们指定了 MySQL JDBC 驱动程序,如下所示:

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

因此,JBoss EAP 会自动将 JDBC 驱动程序安装到部署包 (ROOT.war)。 你可以引用 MySQL JDBC 驱动程序的名称,如下所示:

ROOT.war_com.mysql.cj.jdbc.Driver_8_0

在 JBoss EAP 中创建 MySQL 数据源对象

若要访问 Azure Database for MySQL,你需要在 JBoss EAP 中配置 DataSource 对象,并在源代码中指定 JNDI 名称。

为了在 JBoss EAP 中创建 MySQL DataSource 对象,我们创建了以下启动 shell 脚本。 该脚本文件是 /WEB-INF 目录下的 createMySQLDataSource.sh

备注

在脚本中,我们将使用 JBoss CLI 命令绑定 MySQL 数据源。 连接字符串、用户名和密码使用环境变量 MYSQL_CONNECTION_URLMYSQL_USERMYSQL_PASSWORD

接下来会显示脚本文件的源。 此脚本文件已上传到应用服务,但尚未配置为调用。

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

现在,配置你的应用服务实例以调用启动脚本:

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

在脚本运行后,每次重启应用程序服务器时,都会调用该脚本。

备注

如果你的部署项目不是 ROOT.war,则还需要更改 --driver-name=YOUR_ARTIFACT.war_com.mysql.cj.jdbc.Driver_8_0 值。

配置环境变量以便连接到 MySQL

配置启动脚本后,将应用服务配置为使用某些环境变量:

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

提示

MYSQL_CONNECTION_URLMYSQL_USERMYSQL_PASSWORD 的值是在上一个单元中设置的。

确认代码中的数据源引用

要从应用程序访问 MySQL 数据库,需要在应用程序项目中配置数据源引用。 我们使用 Java 持久化 API (JPA) 实现了数据库访问代码。

DataSource 引用的配置已添加到 JPA 的配置文件 persistence.xml 中。

访问以下文件:

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

检查 DataSource 名称是否与配置中使用的名称匹配。 代码已将 JNDI 名称创建为 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>

然后,可以访问 PersistenceContext 单元名称中引用的 MySQL 数据库,如下所示:

@Transactional(REQUIRED)
@RequestScoped
public class CityService {

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

访问应用程序

在示例应用程序中,我们实现了三个 REST 终结点。 你可以访问应用程序,并使用 Web 浏览器或 curl 命令来验证这些终结点。

若要访问应用程序,需要引用你在前面的部分获得的应用程序 URL:

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

运行以下命令,获取 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"]$ 

如果在 URL 中指定洲,则可以获取指定洲内的所有国家/地区。

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

最后,如果在 /countries 后面指定国家/地区代码,则可以得到该国家/地区内所有人口大于一百万的城市。

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"

练习摘要

你现在已经验证了应用程序 REST 终结点,并测试了应用程序可从 MySQL 数据库中获取数据。

在下一单元中,你将检查服务器日志。