練習 - 將 Java EE (Jakarta EE) 應用程式部署到 Azure App Service 上的 JBoss EAP

已完成

在本練習中,您會將 Java EE (Jakarta EE) 應用程式部署到 Azure App Service 上的 JBoss EAP。 您使用 Maven 外掛程式來設定專案、編譯和部署應用程式,以及設定資料來源。

設定應用程式搭配適用於 Azure App Service 的 Maven 外掛程式

讓我們透過在適用於 Azure App Service 的 Maven 外掛程式中執行設定目標,以設定應用程式。

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

重要

如果您變更了 MySQL 伺服器的區域,應該也變更為 Java EE 應用程式伺服器的相同區域,將延遲降至最低。
在命令中,針對 [JAVA 版本] 選取 [JAVA 11],並針對執行階段堆疊選取 JBoss EAP 7.2。

Input 元素
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 App Service 部署設定之後,請編譯並封裝原始程式碼。

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

在 Azure App Service 上將 Java EE 應用程式部署至 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 DataSource 物件

若要存取適用於 MySQL 的 Azure 資料庫,您需要在 JBoss EAP 中設定 DataSource 物件,並在原始程式碼中指定 JNDI 名稱。

為了在 JBoss EAP 中建立 MySQL DataSource 物件,我們建立了下列啟動殼層指令碼。 指令檔 createMySQLDataSource.sh 位於 /WEB-INF 目錄下。

注意

在指令碼中,我們使用 JBoss CLI 命令繫結 MySQL DataSource。 連接字串、使用者名稱和密碼會使用環境變數 MYSQL_CONNECTION_URLMYSQL_USERMYSQL_PASSWORD

接下來會顯示指令檔的來源。 此指令檔已上傳至 App Service,但尚未設定為叫用。

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

現在設定您的 App Service 執行個體,以叫用啟動指令碼:

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

設定啟動指令碼之後,請將 App Service 設定為使用特定環境變數:

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值已在上一個單元中設定。

確認程式碼中的 DataSource 參考

若要從您的應用程式存取 MySQL 資料庫,必須在應用程式專案中設定資料來源參考。 我們使用 Java 持續性 API (JPA) 來實作資料庫存取程式碼。

DataSource 參考的設定已新增至 persistence.xml,也就是 JPA 的設定檔。

存取以下檔案:

├── 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 端點。 您可以使用網頁瀏覽器或 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 資料庫取得資料。

在下一個單元中,您將檢查伺服器記錄。