Skip to content

selvasingh/migrate-Java-EE-app-to-azure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

60 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

services platforms author
app-service, PostgreSQL, MySQL
java
selvasingh, sadigopu

Migrate Java EE App to Azure

This guide walks you through the process of migrating an existing Java EE workload to Azure, aka:

  • Java EE app to App Service Linux and
  • App's data to Azure Database for PostgreSQL, MySQL and or SQL Database.

Table of Contents

What you will migrate to cloud

You will migrate the famous Sun's 2003 Java EE Blue Print sample app. The most recent incarnation of the sample uses:

  • Java SE 8
  • Java EE 7
  • JSR 338 Java Persistence API (JPA 2.2)
  • JSR 346 Context and Dependency Injection (CDI 1.1)
  • JSR 345 Enterprise Java Beans 3.2 (EJB 3.2)
  • JSR 344 Java Server Faces (JSF 2.2)
  • JSR 339 Java API for RESTful Web Services (JAX-RS 2.0)
  • Twitter Bootstrap (Bootstrap 3.x, JQuery 2.x, PrimeFaces 6.x)

Upon migration, you will power the app using Azure Database for PostgreSQL, MySQL and or SQL Database.

What you will need

In order to deploy a Java Web app to cloud, you need an Azure subscription. If you do not already have an Azure subscription, you can activate your MSDN subscriber benefits or sign up for a free Azure account.

In addition, you will need the following:

| Azure CLI | Java 8 | Maven 3 | Git | PostgreSQL CLI | MySQL CLI |

Getting Started

You can start from scratch and complete each step, or you can bypass basic setup steps that you are already familiar with. Either way, you will end up with working code.

When you are finished, you can check your results against YOUR code in migrate-Java-EE-app-to-azure/complete.

You can start from migrate-Java-EE-app-to-azure/initial-h2. Or, you can clone from agoncal-application-petstore-ee7

Step ONE - Clone and Prep

git clone --recurse-submodules https://github.com/Azure-Samples/migrate-Java-EE-app-to-azure.git
cd migrate-Java-EE-app-to-azure
yes | cp -rf .prep/* .

Build and Deploy Pet Store Powered Using H2

Open the initial-h2/agoncal-application-petstore-ee7 sample app in your favorite IDE - IntelliJ | Eclipse | VS Code.

Build Pet Store

cd initial-h2/agoncal-application-petstore-ee7

bash-3.2$ mvn package -Dmaven.test.skip=true
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ petstoreee7 ---
...
...
[INFO] --- maven-war-plugin:3.1.0:war (default-war) @ petstoreee7 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [petstoreee7] in [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-h2/agoncal-application-petstore-ee7/target/applicationPetstore]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-h2/agoncal-application-petstore-ee7/src/main/webapp]
[INFO] Webapp assembled in [440 msecs]
[INFO] Building war: /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-h2/agoncal-application-petstore-ee7/target/applicationPetstore.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.340 s
[INFO] Finished at: 2018-12-21T14:46:35-08:00
[INFO] Final Memory: 33M/326M
[INFO] ------------------------------------------------------------------------

Deploy to WildFly in App Service Linux

Login to Azure by using the 'az login' command and follow the instructions that give a device code to be entered in browser

az login

Set environment variables for binding secrets at runtime, particularly Azure resource group and Web app names. You can export them to your local environment, say using the supplied Bash shell script template.

mkdir .scripts
cp set-env-variables-template.sh .scripts/set-env-variables.sh

Modify .scripts/set-env-variables.sh and set Azure Resource Group name, App Service Web App Name, Azure Region and WildFly directory in the local machine. Then, set environment variables:

source .scripts/set-env-variables.sh

Add Maven Plugin for Azure App Service configuration to POM.xml and deploy Pet Store to WildFly in App Service Linux:

<plugins> 

    <!--*************************************************-->
    <!-- Deploy to WildFly in App Service Linux          -->
    <!--*************************************************-->
       
    <plugin>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-webapp-maven-plugin</artifactId>
        <version>1.6.0</version>
        <configuration>
    
            <!-- Web App information -->
            <resourceGroup>${RESOURCEGROUP_NAME}</resourceGroup>
            <appServicePlanName>${WEBAPP_PLAN_NAME}</appServicePlanName>
            <appName>${WEBAPP_NAME}</appName>
            <region>${REGION}</region>
    
            <!-- Java Runtime Stack for Web App on Linux-->
            <linuxRuntime>wildfly 14-jre8</linuxRuntime>
    
        </configuration>
    </plugin>
    ...
</plugins>

Deploy to App Service Linux:

mvn azure-webapp:deploy

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- azure-webapp-maven-plugin:1.6.0:deploy (default-cli) @ petstoreee7 ---
[INFO] Authenticate with Azure CLI 2.0
[INFO] Target Web App doesn't exist. Creating a new one...
[INFO] Creating App Service Plan 'petstore-java-ee-appservice-plan'...
[INFO] Successfully created App Service Plan.
[INFO] Successfully created Web App.
[INFO] Trying to deploy artifact to petstore-java-ee...
[INFO] Deploying the war file...
[INFO] Successfully deployed the artifact to https://petstore-java-ee.azurewebsites.net
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:19 min
[INFO] Finished at: 2018-12-21T14:51:08-08:00
[INFO] Final Memory: 61M/628M
[INFO] ------------------------------------------------------------------------

Open Pet Store running on App Service in App Service Linux

open https://petstore-java-ee.azurewebsites.net

When you are finished, you can check your results against YOUR code in migrate-Java-EE-app-to-azure/initial-postgresql.

Build and Deploy Pet Store Powered Using Azure Database for PostgreSQL

Start your next leg of your journey ... change directory:

cd ../../initial-postgresql/agoncal-application-petstore-ee7

Add PostgreSQL Profile

Add a new profile for PostgreSQL in pom.xml:

<profile>
  <id>postgresql</id>
  <activation>
    <property>
      <name>db</name>
      <value>postgresql</value>
    </property>
  </activation>
  <build>

    <plugins>

      <!-- copy the correct persistence.xml file -->
      <plugin>
        <groupId>com.coderplus.maven.plugins</groupId>
        <artifactId>copy-rename-maven-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <id>copy-file</id>
            <phase>validate</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <sourceFile>${project.basedir}/src/main/resources/META-INF/persistence-postgresql.xml</sourceFile>
              <destinationFile>${project.basedir}/src/main/resources/META-INF/persistence.xml</destinationFile>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>
</profile>

Set environment variables for binding secrets at runtime

cp set-env-variables-template.sh .scripts/set-env-variables.sh

Modify .scripts/set-env-variables.sh and set Azure Resource Group name, App Service Web App Name, Azure Region, FTP details in the local machine and PostgreSQL server info. Make sure to pick a password that adheres to the following rules : Your password must be at least 8 characters in length. Your password must contain characters from three of the following categories – English uppercase letters, English lowercase letters, numbers (0-9), and non-alphanumeric characters (!, $, #, %, etc.) Your password can not contain all or part of the username ( 3 or more consecutive alphanumeric characters)

Get the FTP details by using the webapp and resource group created in the previous H2-based lab, with the following command, which displays profile values

az webapp deployment list-publishing-profiles -g <resource-group> -n <webapp>

{ ... ... "profileName": "petstore-java-ee - FTP", "publishMethod": "FTP", "publishUrl": "ftp://waws-prod-bay-063.ftp.azurewebsites.windows.net/site/wwwroot", "userName": "petstore-java-ee\$petstore-java-ee", "userPWD": "============MASKED===========================================", "webSystem": "WebSites" }


Store FTP host name, say `waws-prod-bay-063.ftp.azurewebsites.windows.net`, user name and user password in .scripts/set-env-variables.sh file.
 
 Note the ip of the local machine
```bash
curl ifconfig.me

Then, set environment variables:

source .scripts/set-env-variables.sh

Create and Configure Petstore DB in Azure Database for PostgreSQL

Create a Petstore DB using Azure CLI and PostgreSQL CLI:

az postgres server create --resource-group ${RESOURCEGROUP_NAME} \
    --name ${POSTGRES_SERVER_NAME} \
    --location ${REGION} \
    --admin-user ${POSTGRES_SERVER_ADMIN_LOGIN_NAME} \
    --admin-password ${POSTGRES_SERVER_ADMIN_PASSWORD} \
    --sku-name GP_Gen4_2 --version 9.6

az postgres server firewall-rule create \
    --resource-group ${RESOURCEGROUP_NAME} \
    --server ${POSTGRES_SERVER_NAME} --name allAzureIPs \
    --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0

curl ifconfig.me

az postgres server firewall-rule create \
    --resource-group ${RESOURCEGROUP_NAME} \
    --server ${POSTGRES_SERVER_NAME} --name myDevBox \
    --start-ip-address ${DEVBOX_IP_ADDRESS} --end-ip-address ${DEVBOX_IP_ADDRESS}

psql --host=${POSTGRES_SERVER_FULL_NAME} --port=5432 \
    --username=${POSTGRES_SERVER_ADMIN_FULL_NAME} \
    --dbname=${POSTGRES_DATABASE_NAME} --set=sslmode=require

Password for user postgres@petstore-db:
psql (11.1, server 9.6.10)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=> \l
                                                               List of databases
       Name        |      Owner      | Encoding |          Collate           |           Ctype            |          Access privileges          
-------------------+-----------------+----------+----------------------------+----------------------------+-------------------------------------
 azure_maintenance | azure_superuser | UTF8     | English_United States.1252 | English_United States.1252 | azure_superuser=CTc/azure_superuser
 azure_sys         | azure_superuser | UTF8     | English_United States.1252 | English_United States.1252 | 
 postgres          | azure_superuser | UTF8     | English_United States.1252 | English_United States.1252 | 
 template0         | azure_superuser | UTF8     | English_United States.1252 | English_United States.1252 | =c/azure_superuser                 +
                   |                 |          |                            |                            | azure_superuser=CTc/azure_superuser
 template1         | azure_superuser | UTF8     | English_United States.1252 | English_United States.1252 | =c/azure_superuser                 +
                   |                 |          |                            |                            | azure_superuser=CTc/azure_superuser
(5 rows)

postgres=> \q

When you migrate Java workloads to cloud, you will be considering moving data to cloud. To accelerate your transition to cloud, Azure offers plenty of options to migrate your data to cloud.

Also, for your convenience, there is a cheat sheet for PostgreSQL CLI.

Configure PostgreSQL Data Source

There are 5 steps to configure a data source. These steps are similar to configuring data sources in any on premise Java EE app servers:

Step 1: Understand How to configure WildFly

In App Service, each instance of an app server is stateless. Therefore, each instance must be configured on startup to support a Wildfly configuration needed by your application. You can configure at startup by supplying a startup Bash script that calls JBoss/WildFly CLI commands to setup data sources, messaging providers and any other dependencies. We will create a startup.sh script and place it in the /home directory of the Web app. The script will:

Install a WildFly module:

# where resources point to JDBC driver for PostgreSQL
# and module xml points to module description, see below

module add --name=org.postgres --resources=/home/site/deployments/tools/postgresql-42.2.5.jar --module-xml=/home/site/deployments/tools/postgresql-module.xml

Where postgresql-module.xml describes the module:

<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="org.postgres">
    <resources>
     <!-- ***** IMPORTANT : PATH should point to PostgreSQL Java driver on App Service Linux *******-->
       <resource-root path="/home/site/deployments/tools/postgresql-42.2.5.jar" />
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>

Add a JDBC driver for PostgreSQL:

/subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource)

Install a data source by using the data-source shortcut command:

data-source add --name=postgresDS --driver-name=postgres --jndi-name=java:jboss/datasources/postgresDS --connection-url=${POSTGRES_CONNECTION_URL,env.POSTGRES_CONNECTION_URL:jdbc:postgresql://db:5432/postgres} --user-name=${POSTGRES_SERVER_ADMIN_FULL_NAME,env.POSTGRES_SERVER_ADMIN_FULL_NAME:postgres} --password=${POSTGRES_SERVER_ADMIN_PASSWORD,env.POSTGRES_SERVER_ADMIN_PASSWORD:example} --use-ccm=true --max-pool-size=5 --blocking-timeout-wait-millis=5000 --enabled=true --driver-class=org.postgresql.Driver --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter --jta=true --use-java-context=true --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker

A server reload may be required for the changes to take effect:

reload --use-current-server-config=true

These JBoss CLI commands, JDBC driver for PostgreSQL and module XML are available in initial-postgresql/agoncal-application-petstore-ee7/.scripts

Also, you can directly download the latest version of JDBC driver for PostgreSQL

Step 2: Upload data source artifacts to App Service linux

Open an FTP connection to App Service Linux to upload data source artifacts:

pwd
/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7

cd .scripts

ftp
ftp> open waws-prod-bay-063.drip.azurewebsites.windows.net
Trying 23.99.84.148...
Connected to waws-prod-bay-063.drip.azurewebsites.windows.net.
220 Microsoft FTP Service
Name (waws-prod-bay-063.drip.azurewebsites.windows.net:selvasingh): 
331 Password required
Password: 
230 User logged in.
Remote system type is Windows_NT.
ftp> ascii
200 Type set to A.

ftp> passive

# Upload startup.sh to /home directory
ftp> put startup.sh
local: startup.sh remote: startup.sh
229 Entering Extended Passive Mode (|||10204|)
125 Data connection already open; Transfer starting.
100% |************************************************|   236       39.33 KiB/s    --:-- ETA
226 Transfer complete.
236 bytes sent in 00:00 (5.01 KiB/s)

# Upload CLI Commands, Module XML and JDBC Driver for PostgreSQL to /home/site/deployments/tools
ftp> cd site/deployments/tools
250 CWD command successful.
ftp> put postgresql-datasource-commands.cli 
local: postgresql-datasource-commands.cli remote: postgresql-datasource-commands.cli
229 Entering Extended Passive Mode (|||10205|)
125 Data connection already open; Transfer starting.
100% |************************************************|  1444      234.94 KiB/s    --:-- ETA
226 Transfer complete.
1444 bytes sent in 00:00 (32.31 KiB/s)
ftp> put postgresql-module.xml 
local: postgresql-module.xml remote: postgresql-module.xml
229 Entering Extended Passive Mode (|||10206|)
125 Data connection already open; Transfer starting.
100% |************************************************|   404      192.17 KiB/s    --:-- ETA
226 Transfer complete.
404 bytes sent in 00:00 (5.86 KiB/s)
ftp> binary
200 Type set to I.
ftp> put postgresql-42.2.5.jar 
local: postgresql-42.2.5.jar remote: postgresql-42.2.5.jar
229 Entering Extended Passive Mode (|||10207|)
125 Data connection already open; Transfer starting.
100% |************************************************|   806 KiB  506.52 KiB/s    00:00 ETA
226 Transfer complete.
825943 bytes sent in 00:01 (469.59 KiB/s)
ftp> bye
221 Goodbye.
Step 3: Set PostgreSQL database connection info in the Web app environment

Use Azure CLI to set database connection info:

az webapp config appsettings set \
 --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} \
 --settings \
 POSTGRES_CONNECTION_URL=${POSTGRES_CONNECTION_URL} \
 POSTGRES_SERVER_ADMIN_PASSWORD=${POSTGRES_SERVER_ADMIN_PASSWORD} \
 POSTGRES_SERVER_ADMIN_FULL_NAME=${POSTGRES_SERVER_ADMIN_FULL_NAME}

[
 {
   "name": "WEBSITE_HTTPLOGGING_RETENTION_DAYS",
   "slotSetting": false,
   "value": "3"
 },
 {
   "name": "POSTGRES_CONNECTION_URL",
   "slotSetting": false,
   "value": "jdbc:postgresql://petstore-db.postgres.database.azure.com:5432/postgres?ssl=true"
 },
 {
   "name": "POSTGRES_SERVER_ADMIN_PASSWORD",
   "slotSetting": false,
   "value": "======= MASKED ======="
 },
 {
   "name": "POSTGRES_SERVER_ADMIN_FULL_NAME",
   "slotSetting": false,
   "value": "postgres@petstore-db"
 }
] 
 
Step 4: Test the JBoss/WildFly CLI commands to configure data source

You can test Bash script for configuring data source by running them on App Service Linux by opening an SSH connection from your development machine:

# ======== first terminal window =========
az webapp remote-connection create --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} &
[18] 7422
bash-3.2$ Auto-selecting port: 60029
SSH is available { username: root, password: Docker! }
Start your favorite client and connect to port 60029
Websocket tracing disabled, use --verbose flag to enable
Successfully connected to local server..

# ======== second terminal window ========
ssh root@localhost -p 60029
The authenticity of host '[localhost]:60029 ([127.0.0.1]:60029)' can't be established.
ECDSA key fingerprint is SHA256:Lys3Kd4sNJc7X8LVMRP89GKbOzlOGp03tGYj+mY4Kic.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:60029' (ECDSA) to the list of known hosts.
root@localhost's password:
  _____
  /  _  \ __________ _________   ____
 /  /_\  \___   /  |  \_  __ \_/ __ \
/    |    \/    /|  |  /|  | \/\  ___/
\____|__  /_____ \____/ |__|    \___  >
        \/      \/                  \/
A P P   S E R V I C E   O N   L I N U X

Documentation: http://aka.ms/webapp-linux
c315a18b39d2:/home#

#========= open a vi window to edit startup.sh ============
c315a18b39d2:/home# vi startup.sh

# ======== vi window =====================
#!/usr/bin/env bash^M
/opt/jboss/wildfly/bin/jboss-cli.sh -c --file=/home/site/deployments/tools/postgresql-datasource-commands.cli^M
~

remove those '^M' end of line characters and save the file

# ======== run JBoss/WildFly CLI commands to configure a data source ===========

c315a18b39d2:/home# /opt/jboss/wildfly/bin/jboss-cli.sh --connect
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true

[standalone@localhost:9990 /] module add --name=org.postgres --resources=/home/site/deployments/tools/postgresql-42.2.5.jar --module-xml=/home/site/deployments/tools/postgresql-module.xml
[standalone@localhost:9990 /] /subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver,driver-xa-datasource-class-name=org.postgresql.xa.PGXADataSource)
{"outcome" => "success"}

[standalone@localhost:9990 /] data-source add --name=postgresDS --driver-name=postgres --jndi-name=java:jboss/datasources/postgresDS --connection-url=${POSTGRES_CONNECTION_URL,env.POSTGRES_CONNECTION_URL:jdbc:postgresql://db:5432/postgres} --user-name=${POSTGRES_SERVER_ADMIN_FULL_NAME,env.POSTGRES_SERVER_ADMIN_FULL_NAME:postgres} --password=${POSTGRES_SERVER_ADMIN_PASSWORD,env.POSTGRES_SERVER_ADMIN_PASSWORD:example} --use-ccm=true --max-pool-size=5 --blocking-timeout-wait-millis=5000 --enabled=true --driver-class=org.postgresql.Driver --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter --jta=true --use-java-context=true --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker
[standalone@localhost:9990 /] /subsystem=datasources/data-source=postgresDS:test-connection-in-pool()
{
    "outcome" => "success",
    "result" => [true]
}
Step 5: Restart the remote WildFly app server

Use Azure CLI to restart the remote WildFly app server:

az webapp stop -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}
az webapp start -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}

For additional info, please refer to:

Build PetStore to interact with Azure Database for PostgreSQL

# Use the Maven profile for PostgreSQL to build

mvn package -Dmaven.test.skip=true -Ddb=postgresql
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- copy-rename-maven-plugin:1.0:copy (copy-file) @ petstoreee7 ---
[INFO] Copied /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence-postgresql.xml to /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence.xml
...
...
[INFO] --- maven-war-plugin:3.1.0:war (default-war) @ petstoreee7 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [petstoreee7] in [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7/target/applicationPetstore]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7/src/main/webapp]
[INFO] Webapp assembled in [243 msecs]
[INFO] Building war: /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-postgresql/agoncal-application-petstore-ee7/target/applicationPetstore.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.161 s
[INFO] Finished at: 2018-12-21T16:27:42-08:00
[INFO] Final Memory: 31M/513M
[INFO] ------------------------------------------------------------------------

Deploy to App Service Linux

Deploy to WildFly in App Service Linux:

mvn azure-webapp:deploy
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- azure-webapp-maven-plugin:1.6.0:deploy (default-cli) @ petstoreee7 ---
[INFO] Authenticate with Azure CLI 2.0
[INFO] Updating target Web App...
[INFO] Successfully updated Web App.
[INFO] Trying to deploy artifact to petstore-java-ee...
[INFO] Deploying the war file...
[INFO] Successfully deployed the artifact to https://petstore-java-ee.azurewebsites.net
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:07 min
[INFO] Finished at: 2018-12-21T16:45:12-08:00
[INFO] Final Memory: 60M/631M
[INFO] ------------------------------------------------------------------------

Open Pet Store running on App Service Linux and interacting with Azure Database for PostgreSQL

open https://petstore-java-ee.azurewebsites.net

Log into Azure Database for PostgreSQL and Validate Tables were Created and Populated

psql --host=${POSTGRES_SERVER_FULL_NAME} --port=5432 \
     --username=${POSTGRES_SERVER_ADMIN_FULL_NAME} \
     --dbname=${POSTGRES_DATABASE_NAME} --set=sslmode=require
Password for user postgres@petstore-db: 
psql (11.1, server 9.6.10)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=> \l
postgres=> \dt
               List of relations
 Schema |        Name        | Type  |  Owner   
--------+--------------------+-------+----------
 public | category           | table | postgres
 public | country            | table | postgres
 public | customer           | table | postgres
 public | item               | table | postgres
 public | order_line         | table | postgres
 public | product            | table | postgres
 public | purchase_order     | table | postgres
 public | t_order_order_line | table | postgres
(8 rows)

postgres=> select name from category;
   name   
----------
 Fish
 Dogs
 Reptiles
 Cats
 Birds
(5 rows)

postgres=> \q
bash-3.2$ 

Trouble Shoot Petstore on Azure by Viewing Logs

Configure logs for the deployed Java Web app in App Service Linux:

az webapp log config --name ${WEBAPP_NAME} \
 --resource-group ${RESOURCEGROUP_NAME} \
  --web-server-logging filesystem

Open Java Web app remote log stream from a local machine:

az webapp log tail --name ${WEBAPP_NAME} \
 --resource-group ${RESOURCEGROUP_NAME}

2018-12-22T00:47:48  Welcome, you are now connected to log-streaming service.
2018-12-22T00:41:45.064280703Z   _____                               
2018-12-22T00:41:45.064325203Z   /  _  \ __________ _________   ____  
2018-12-22T00:41:45.064331403Z  /  /_\  \___   /  |  \_  __ \_/ __ \ 
2018-12-22T00:41:45.064335603Z /    |    \/    /|  |  /|  | \/\  ___/ 
2018-12-22T00:41:45.064339403Z \____|__  /_____ \____/ |__|    \___  >
2018-12-22T00:41:45.064343503Z         \/      \/                  \/ 
2018-12-22T00:41:45.064347403Z A P P   S E R V I C E   O N   L I N U X  
...
...
2018-12-22T00:41:47.124232726Z ***Copying /home/site/wwwroot/webapps/ROOT to /opt/jboss/wildfly/standalone/deployments/ROOT.war
2018-12-22T00:41:48.193646648Z ***Creating marker file /opt/jboss/wildfly/standalone/deployments/ROOT.war.dodeploy
...
...
2018-12-22T00:41:52.895075005Z 00:41:52,770 INFO  [org.jboss.modules] (main) JBoss Modules version 1.8.6.Final
2018-12-22T00:41:55.713960188Z 00:41:55,704 INFO  [org.jboss.msc] (main) JBoss MSC version 1.4.3.Final
2018-12-22T00:41:55.817008239Z 00:41:55,816 INFO  [org.jboss.threads] (main) JBoss Threads version 2.3.2.Final
2018-12-22T00:41:57.201980849Z 00:41:57,201 INFO  [org.jboss.as] (MSC service thread 1-2) WFLYSRV0049: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) starting
...
...
2018-12-22T00:42:19.043814396Z 00:42:19,043 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 41) WFLYJCA0005: Deploying non-JDBC-compliant driver class org.postgresql.Driver (version 42.2)
2018-12-22T00:42:19.050046930Z 00:42:19,042 INFO  [org.jboss.as.security] (MSC service thread 1-2) WFLYSEC0001: Current PicketBox version=5.0.3.Final
2018-12-22T00:42:19.086580525Z 00:42:19,086 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0003: Undertow 2.0.13.Final starting
2018-12-22T00:42:19.155234792Z 00:42:19,155 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-2) WFLYJCA0018: Started Driver service with driver-name = h2
2018-12-22T00:42:19.173958592Z 00:42:19,173 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-2) WFLYJCA0018: Started Driver service with driver-name = postgres
...
...
2018-12-22T00:42:40.634081365Z 00:42:40,633 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) started in 22695ms - Started 801 of 972 services (328 services are lazy, passive or on-demand)

When you are finished, you can check your results against YOUR code in migrate-Java-EE-app-to-azure/initial-mysql.

Build and Deploy Pet Store Powered Using Azure Database for MySQL

Start your next leg of your journey ... change directory:

cd ../../initial-mysql/agoncal-application-petstore-ee7

Add MySQL Profile

Add a new profile for MySQL in pom.xml:

<profile>
  <id>mysql</id>
  <activation>
    <property>
      <name>db</name>
      <value>mysql</value>
    </property>
  </activation>
  <build>

    <plugins>

      <!-- copy the MySQL persistence.xml file -->
      <plugin>
        <groupId>com.coderplus.maven.plugins</groupId>
        <artifactId>copy-rename-maven-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <id>copy-file</id>
            <phase>validate</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <sourceFile>${project.basedir}/src/main/resources/META-INF/persistence-mysql.xml</sourceFile>
              <destinationFile>${project.basedir}/src/main/resources/META-INF/persistence.xml</destinationFile>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>
</profile>

Set environment variables for binding secrets at runtime

cp set-env-variables-template.sh .scripts/set-env-variables.sh

Modify .scripts/set-env-variables.sh and set Azure Resource Group name, App Service Web App Name, Azure Region, WildFly directory in the local machine, FTP deployment credentials and MySQL server info. Then, set environment variables:

source .scripts/set-env-variables.sh

Create and Configure MySQL DB in Azure Database for MySQL

Create a Petstore DB using Azure CLI and MySQL CLI:

az mysql server create --resource-group ${RESOURCEGROUP_NAME} \
 --name ${MYSQL_SERVER_NAME}  --location westus2 \
 --admin-user ${MYSQL_SERVER_ADMIN_LOGIN_NAME} \
 --admin-password ${MYSQL_SERVER_ADMIN_PASSWORD} \
 --sku-name GP_Gen5_32 \
 --ssl-enforcement Disabled \
 --version 5.7

// allow access from Azure resources
az mysql server firewall-rule create --name allAzureIPs \
 --server ${MYSQL_SERVER_NAME} \
 --resource-group ${RESOURCEGROUP_NAME} \
 --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0

// allow access from your dev machine for testing
az mysql server firewall-rule create --name myDevBox \
 --server ${MYSQL_SERVER_NAME} \
 --resource-group ${RESOURCEGROUP_NAME} \
 --start-ip-address ${DEVBOX_IP_ADDRESS} --end-ip-address ${DEVBOX_IP_ADDRESS}

// increase connection timeout
az mysql server configuration set --name wait_timeout \
 --resource-group ${RESOURCEGROUP_NAME} \
 --server ${MYSQL_SERVER_NAME} --value 2147483

// log into mysql
mysql -u ${MYSQL_SERVER_ADMIN_FULL_NAME}  -h ${MYSQL_SERVER_FULL_NAME} -P 3306 -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 64096
Server version: 5.6.39.0 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE DATABASE petstore;
Query OK, 1 row affected (0.05 sec)

mysql> CREATE USER 'root' IDENTIFIED BY 'petstore';
Query OK, 0 rows affected (0.04 sec)

mysql> GRANT ALL PRIVILEGES ON petstore.* TO 'root';
Query OK, 0 rows affected (0.05 sec)

mysql> quit
Bye

When you migrate Java workloads to cloud, you will be considering moving data to cloud. To accelerate your transition to cloud, Azure offers plenty of options to migrate your data to cloud.

Also, for your convenience, there is a cheat sheet for MySQL CLI.

Configure MySQL Data Source

There are 5 steps to configure a data source. These steps are similar to configuring data sources in any on premise Java EE app servers:

Step 1: Understand How to configure WildFly

In App Service, each instance of an app server is stateless. Therefore, each instance must be configured on startup to support a Wildfly configuration needed by your application. You can configure at startup by supplying a startup Bash script that calls JBoss/WildFly CLI commands to setup data sources, messaging providers and any other dependencies. We will create a startup.sh script and place it in the /home directory of the Web app. The script will:

Install a WildFly module:

# where resources point to JDBC driver for MySQL
# and module xml points to module description, see below

module add --name=com.mysql --resources=/home/site/deployments/tools/mysql-connector-java-8.0.13.jar --module-xml=/home/site/deployments/tools/mysql-module.xml

Where mysql-module.xml describes the module:

<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
    <resources>
     <!-- ***** IMPORTANT : REPLACE THIS PLACEHOLDER *******-->
       <resource-root path="/home/site/deployments/tools/mysql-connector-java-8.0.13.jar" />
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
    </dependencies>
</module>

Add a JDBC driver for MySQL:

/subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql,driver-class-name=com.mysql.cj.jdbc.Driver)

Install a data source by using the data-source shortcut command:

data-source add --name=mysqlDS --jndi-name=java:jboss/datasources/mysqlDS --connection-url=${MYSQL_CONNECTION_URL,env.MYSQL_CONNECTION_URL:jdbc:mysql://db:3306/petstore} --driver-name=mysql --user-name=${MYSQL_SERVER_ADMIN_FULL_NAME,env.MYSQL_SERVER_ADMIN_FULL_NAME:mysql} --password=${MYSQL_SERVER_ADMIN_PASSWORD,env.MYSQL_SERVER_ADMIN_PASSWORD:example} --use-ccm=true --max-pool-size=5 --blocking-timeout-wait-millis=5000 --enabled=true --driver-class=com.mysql.cj.jdbc.Driver --jta=true --use-java-context=true --exception-sorter-class-name=com.mysql.cj.jdbc.integration.jboss.ExtendedMysqlExceptionSorter

A server reload may be required for the changes to take effect:

reload --use-current-server-config=true

These JBoss CLI commands, JDBC driver for MySQL and module XML are available in initial-mysql/agoncal-application-petstore-ee7/.scripts

Also, you can directly download JDBC driver for MySQL. For example:

wget -q "http://search.maven.org/remotecontent?filepath=mysql/mysql-connector-java/8.0.13/mysql-connector-java-8.0.13.jar" -O mysql-connector-java-8.0.13.jar
Step 2: Upload data source artifacts to App Service linux

Open an FTP connection to App Service Linux to upload data source artifacts:

pwd
/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7

cd .scripts

ftp
ftp> open waws-prod-bay-063.drip.azurewebsites.windows.net
Trying 23.99.84.148...
Connected to waws-prod-bay-063.drip.azurewebsites.windows.net.
220 Microsoft FTP Service
Name (waws-prod-bay-063.drip.azurewebsites.windows.net:selvasingh): petstore-java-ee\\$petstore-java-ee
331 Password required
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> ascii
200 Type set to A.
ftp> put startup.sh
local: startup.sh remote: startup.sh
229 Entering Extended Passive Mode (|||10208|)
125 Data connection already open; Transfer starting.
100% |************************************************|   236       40.58 KiB/s    --:-- ETA
226 Transfer complete.
236 bytes sent in 00:00 (5.18 KiB/s)
ftp> cd site/deployments/tools
250 CWD command successful.
ftp> put mysql-datasource-commands.cli
local: mysql-datasource-commands.cli remote: mysql-datasource-commands.cli
229 Entering Extended Passive Mode (|||10209|)
125 Data connection already open; Transfer starting.
100% |************************************************|  1375      226.39 KiB/s    --:-- ETA
226 Transfer complete.
1375 bytes sent in 00:00 (30.81 KiB/s)
ftp> put mysql-module.xml
local: mysql-module.xml remote: mysql-module.xml
229 Entering Extended Passive Mode (|||10210|)
125 Data connection already open; Transfer starting.
100% |************************************************|   411        1.29 MiB/s    --:-- ETA
226 Transfer complete.
411 bytes sent in 00:00 (9.34 KiB/s)
ftp> binary
200 Type set to I.
ftp> put mysql-connector-java-8.0.13.jar
local: mysql-connector-java-8.0.13.jar remote: mysql-connector-java-8.0.13.jar
229 Entering Extended Passive Mode (|||10211|)
125 Data connection already open; Transfer starting.
100% |************************************************|  2082 KiB  622.64 KiB/s    00:00 ETA
226 Transfer complete.
2132635 bytes sent in 00:03 (597.54 KiB/s)
ftp> bye
221 Goodbye.
Step 3: Set MySQL database connection info in the Web app environment

Use Azure CLI to set database connection info:

az webapp config appsettings set \
  --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} \
  --settings \
  MYSQL_CONNECTION_URL=${MYSQL_CONNECTION_URL} \
  MYSQL_SERVER_ADMIN_PASSWORD=${MYSQL_SERVER_ADMIN_PASSWORD} \
  MYSQL_SERVER_ADMIN_FULL_NAME=${MYSQL_SERVER_ADMIN_FULL_NAME}

[
 {
   "name": "WEBSITE_HTTPLOGGING_RETENTION_DAYS",
   "slotSetting": false,
   "value": "3"
 },
 {
   "name": "MYSQL_CONNECTION_URL",
   "slotSetting": false,
   "value": "jdbc:mysql://petstore-db1221.mysql.database.azure.com:3306/petstore?ssl=true"
 },
 {
   "name": "MYSQL_SERVER_ADMIN_PASSWORD",
   "slotSetting": false,
   "value": "======= MASKED ======="
 },
 {
   "name": "MYSQL_SERVER_ADMIN_FULL_NAME",
   "slotSetting": false,
   "value": "selvasingh@petstore-db1221"
 }
] 
 
Step 4: Test the JBoss/WildFly CLI commands to configure data source

You can test Bash script for configuring data source by running them on App Service Linux by opening an SSH connection from your development machine:

# ======== first terminal window =========
az webapp remote-connection create --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} &
[18] 7422
bash-3.2$ Auto-selecting port: 60029
SSH is available { username: root, password: Docker! }
Start your favorite client and connect to port 60029
Websocket tracing disabled, use --verbose flag to enable
Successfully connected to local server..

# ======== second terminal window ========
ssh root@localhost -p 60029
The authenticity of host '[localhost]:60029 ([127.0.0.1]:60029)' can't be established.
ECDSA key fingerprint is SHA256:Lys3Kd4sNJc7X8LVMRP89GKbOzlOGp03tGYj+mY4Kic.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:60029' (ECDSA) to the list of known hosts.
root@localhost's password:
  _____
  /  _  \ __________ _________   ____
 /  /_\  \___   /  |  \_  __ \_/ __ \
/    |    \/    /|  |  /|  | \/\  ___/
\____|__  /_____ \____/ |__|    \___  >
        \/      \/                  \/
A P P   S E R V I C E   O N   L I N U X

Documentation: http://aka.ms/webapp-linux
c315a18b39d2:/home#

#========= open a vi window to edit startup.sh ============
c315a18b39d2:/home# vi startup.sh

# ======= vi window second time ==========
#!/usr/bin/env bash^M
/opt/jboss/wildfly/bin/jboss-cli.sh -c --file=/home/site/deployments/tools/postgresql-datasource-commands.cli^M
/opt/jboss/wildfly/bin/jboss-cli.sh -c --file=/home/site/deployments/tools/mysql-datasource-commands.cli^M
~


remove those '^M' end of line characters and save the file

# ======== run JBoss/WildFly CLI commands to configure a data source ===========

c315a18b39d2:/home# /opt/jboss/wildfly/bin/jboss-cli.sh --connect
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true

[standalone@localhost:9990 /] module add --name=com.mysql --resources=/home/site/deployments/tools/mysql-connector-java-8.0.13.jar --module-xml=/home/site/deployments/tools/mysql-module.xml
[standalone@localhost:9990 /] /subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql,driver-class-name=com.mysql.cj.jdbc.Driver)
{"outcome" => "success"}

[standalone@localhost:9990 /] data-source add --name=mysqlDS --jndi-name=java:jboss/datasources/mysqlDS --connection-url=${MYSQL_CONNECTION_URL,env.MYSQL_CONNECTION_URL:jdbc:mysql://db:3306/petstore} --driver-name=mysql --user-name=${MYSQL_SERVER_ADMIN_FULL_NAME,env.MYSQL_SERVER_ADMIN_FULL_NAME:mysql} --password=${MYSQL_SERVER_ADMIN_PASSWORD,env.MYSQL_SERVER_ADMIN_PASSWORD:example} --use-ccm=true --max-pool-size=5 --blocking-timeout-wait-millis=5000 --enabled=true --driver-class=com.mysql.cj.jdbc.Driver --jta=true --use-java-context=true --exception-sorter-class-name=com.mysql.cj.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
[standalone@localhost:9990 /] /subsystem=datasources/data-source=mysqlDS:test-connection-in-pool()
{
    "outcome" => "success",
    "result" => [true]
}
Step 5: Restart the remote WildFly app server

Use Azure CLI to restart the remote WildFly app server:

az webapp stop -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}
az webapp start -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}

For additional info, please refer to:

Build PetStore to interact with Azure Database for MySQL

# Use the Maven profile for MySQL to build

bash-3.2$ mvn package -Dmaven.test.skip=true -Ddb=mysql
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- copy-rename-maven-plugin:1.0:copy (copy-file) @ petstoreee7 ---
[INFO] Copied /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence-mysql.xml to /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence.xml
...
...
[INFO] --- maven-war-plugin:3.1.0:war (default-war) @ petstoreee7 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [petstoreee7] in [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7/target/applicationPetstore]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7/src/main/webapp]
[INFO] Webapp assembled in [258 msecs]
[INFO] Building war: /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-mysql/agoncal-application-petstore-ee7/target/applicationPetstore.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.199 s
[INFO] Finished at: 2018-12-22T10:47:23-08:00
[INFO] Final Memory: 26M/388M
[INFO] ------------------------------------------------------------------------

Deploy to App Service Linux

Deploy to WildFly in App Service Linux:

mvn azure-webapp:deploy
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- azure-webapp-maven-plugin:1.6.0:deploy (default-cli) @ petstoreee7 ---
[INFO] Authenticate with Azure CLI 2.0
[INFO] Updating target Web App...
[INFO] Successfully updated Web App.
[INFO] Trying to deploy artifact to petstore-java-ee...
[INFO] Deploying the war file...
[INFO] Successfully deployed the artifact to https://petstore-java-ee.azurewebsites.net
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 56.128 s
[INFO] Finished at: 2018-12-22T10:51:13-08:00
[INFO] Final Memory: 60M/626M
[INFO] ------------------------------------------------------------------------

Open Pet Store running on App Service Linux and interacting with Azure Database for MySQL

open https://petstore-java-ee.azurewebsites.net

Log into Azure Database for MySQL and Validate Tables were Created and Populated

mysql -u ${MYSQL_SERVER_ADMIN_FULL_NAME}  -h ${MYSQL_SERVER_FULL_NAME} -P 3306 -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 64167
Server version: 5.6.39.0 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use petstore;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+--------------------+
| Tables_in_petstore |
+--------------------+
| category           |
| country            |
| customer           |
| hibernate_sequence |
| item               |
| order_line         |
| product            |
| purchase_order     |
| t_order_order_line |
+--------------------+
9 rows in set (0.03 sec)

mysql> select name from category;
+----------+
| name     |
+----------+
| Fish     |
| Dogs     |
| Reptiles |
| Cats     |
| Birds    |
+----------+
5 rows in set (0.03 sec)

mysql> quit
Bye

Trouble Shoot Petstore on Azure by Viewing Logs

Open Java Web app remote log stream from a local machine:

az webapp log tail --name ${WEBAPP_NAME} \
 --resource-group ${RESOURCEGROUP_NAME}

2018-12-22T00:47:48  Welcome, you are now connected to log-streaming service.
2018-12-22T00:41:45.064280703Z   _____                               
2018-12-22T00:41:45.064325203Z   /  _  \ __________ _________   ____  
2018-12-22T00:41:45.064331403Z  /  /_\  \___   /  |  \_  __ \_/ __ \ 
2018-12-22T00:41:45.064335603Z /    |    \/    /|  |  /|  | \/\  ___/ 
2018-12-22T00:41:45.064339403Z \____|__  /_____ \____/ |__|    \___  >
2018-12-22T00:41:45.064343503Z         \/      \/                  \/ 
2018-12-22T00:41:45.064347403Z A P P   S E R V I C E   O N   L I N U X  
...
...
2018-12-22T00:41:47.124232726Z ***Copying /home/site/wwwroot/webapps/ROOT to /opt/jboss/wildfly/standalone/deployments/ROOT.war
2018-12-22T00:41:48.193646648Z ***Creating marker file /opt/jboss/wildfly/standalone/deployments/ROOT.war.dodeploy
...
...
2018-12-22T00:41:52.895075005Z 00:41:52,770 INFO  [org.jboss.modules] (main) JBoss Modules version 1.8.6.Final
2018-12-22T00:41:55.713960188Z 00:41:55,704 INFO  [org.jboss.msc] (main) JBoss MSC version 1.4.3.Final
2018-12-22T00:41:55.817008239Z 00:41:55,816 INFO  [org.jboss.threads] (main) JBoss Threads version 2.3.2.Final
2018-12-22T00:41:57.201980849Z 00:41:57,201 INFO  [org.jboss.as] (MSC service thread 1-2) WFLYSRV0049: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) starting
...
...
2018-12-22T00:42:19.043814396Z 00:42:19,043 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 41) WFLYJCA0005: Deploying non-JDBC-compliant driver class com.mysql.cj.jdbc.Driver (version 8.0)
2018-12-22T00:42:19.050046930Z 00:42:19,042 INFO  [org.jboss.as.security] (MSC service thread 1-2) WFLYSEC0001: Current PicketBox version=5.0.3.Final
2018-12-22T00:42:19.086580525Z 00:42:19,086 INFO  [org.wildfly.extension.undertow] (MSC service thread 1-1) WFLYUT0003: Undertow 2.0.13.Final starting
2018-12-22T00:42:19.155234792Z 00:42:19,155 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-2) WFLYJCA0018: Started Driver service with driver-name = h2
2018-12-22T00:42:19.173958592Z 00:42:19,173 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-2) WFLYJCA0018: Started Driver service with driver-name = mysql
2018-12-22T00:42:19.173958592Z 00:42:19,187 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-1) WFLYJCA0018: Started Driver service with driver-name = postgres
...
...
2018-12-22T00:42:40.634081365Z 00:42:40,633 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) started in 22695ms - Started 801 of 972 services (328 services are lazy, passive or on-demand)

Build and Deploy Pet Store Powered Using Azure SQL Database

Start your next leg of your journey ... change directory:

cd ../../initial-sql/agoncal-application-petstore-ee7

Add SQL Database Profile

Add a new profile for sql in pom.xml:

<profile>
  <id>sql</id>
  <activation>
  <property>
    <name>db</name>
    <value>sql</value>
  </property>
  </activation>
  <build>

    <plugins>

      <!-- copy the correct persistence.xml file -->
      <plugin>
        <groupId>com.coderplus.maven.plugins</groupId>
        <artifactId>copy-rename-maven-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <id>copy-file</id>
            <phase>validate</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <sourceFile>${project.basedir}/src/main/resources/META-INF/persistence-sql.xml</sourceFile>
              <destinationFile>${project.basedir}/src/main/resources/META-INF/persistence.xml</destinationFile>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

Set environment variables for binding secrets at runtime

cp set-env-variables-template.sh .scripts/set-env-variables.sh

Modify .scripts/set-env-variables.sh and set Azure Resource Group name, App Service Web App Name, Azure Region, WildFly directory in the local machine, FTP deployment credentials and SQL Database info. Then, set environment variables:

source .scripts/set-env-variables.sh

Create and Configure SQL DB in Azure SQL Database

Install the Azure CLI db-up extension:

az extension add --name db-up

Create a Petstore DB using Azure CLI and sql CLI:

az sql up --resource-group ${RESOURCEGROUP_NAME} \
 --server-name ${SQL_SERVER_NAME} \
 --database-name ${SQL_DATABASE_NAME} --admin-user ${SQL_SERVER_ADMIN_LOGIN_NAME} \
 --admin-password ${SQL_SERVER_ADMIN_PASSWORD} \
 --location ${REGION}

Creating SQL Server 'petstore-sql-0601' in group 'e2e-java-ee'...
Configuring server firewall rule, 'azure-access', to accept connections from all Azure resources...
Creating SQL database 'sqldb'...
Checking your ip address...
Configuring server firewall rule, 'devbox', to allow for your ip address: 98.225.36.126
If SQL server declines your IP address, please create a new firewall rule using:
    `az sql server firewall-rule create -g e2e-java-ee -s petstore-sql-0601 -n {rule_name} --start-ip-address {ip_address} --end-ip-address {ip_address}`
Successfully Connected to SQL Database.
Ran Database Query: `CREATE USER root WITH PASSWORD = "======= MASKED ======="
Ran Database Query: `GRANT ALL TO root`
...
...

When you migrate Java workloads to cloud, you will be considering moving data to cloud. To accelerate your transition to cloud, Azure offers plenty of options to migrate your data to cloud.

Also, for your convenience, there is a cheat sheet for sqlcmd CLI.

Configure SQL Database Data Source

There are 5 steps to configure a data source. These steps are similar to configuring data sources in any on premise Java EE app servers:

Step 1: Understand How to configure WildFly

In App Service, each instance of an app server is stateless. Therefore, each instance must be configured on startup to support a Wildfly configuration needed by your application. You can configure at startup by supplying a startup Bash script that calls JBoss/WildFly CLI commands to setup data sources, messaging providers and any other dependencies. We will create a startup.sh script and place it in the /home directory of the Web app. The script will:

Install a WildFly module:

# where resources point to JDBC driver for SQL Database
# and module xml points to module description, see below

module add --name=com.microsoft --resources=/home/site/deployments/tools/mssql-jdbc-7.2.1.jre8.jar --module-xml=/home/site/deployments/tools/mssql-module.xml

Where mssql-module.xml describes the module:

<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.microsoft">
  <resources>
	<resource-root path="/home/site/deployments/tools/mssql-jdbc-7.2.1.jre8.jar"/>
  </resources>
  <dependencies>
    <module name="javax.api"/>
    <module name="javax.transaction.api"/>
  </dependencies>
</module>

Add a JDBC driver for SQL Database:

/subsystem=datasources/jdbc-driver=sqlserver:add(driver-name="sqlserver",driver-module-name="com.microsoft",driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver,driver-datasource-class-name=com.microsoft.sqlserver.jdbc.SQLServerDataSource)

Install a data source by using the data-source shortcut command:

data-source add --name=sqlDS --jndi-name=java:jboss/datasources/sqlDS --driver-name=sqlserver --connection-url=${SQL_CONNECTION_URL,env.SQL_CONNECTION_URL:example} --validate-on-match=true --background-validation=false --valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLValidConnectionChecker --exception-sorter-class-name=org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLExceptionSorter

A server reload may be required for the changes to take effect:

reload --use-current-server-config=true

These JBoss CLI commands, JDBC driver for SQL Database and module XML are available in initial-sql/agoncal-application-petstore-ee7/.scripts

Also, you can directly download JDBC driver for SQL Database. For example:

Step 2: Upload data source artifacts to App Service linux

Open an FTP connection to App Service Linux to upload data source artifacts:

pwd
/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7

cd .scripts

ftp
ftp> open waws-prod-bay-063.ftp.azurewebsites.windows.net
Trying 23.99.87.125...
Connected to waws-prod-bay-063.drip.azurewebsites.windows.net.
220 Microsoft FTP Service
Name (waws-prod-bay-063.ftp.azurewebsites.windows.net:selvasingh):
331 Password required
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> pwd
Remote directory: /
ftp> ascii
200 Type set to A.
ftp> put startup.sh
local: startup.sh remote: startup.sh
229 Entering Extended Passive Mode (|||10199|)
125 Data connection already open; Transfer starting.
100% |*******************************************************|   125       21.36 KiB/s    --:-- ETA
226 Transfer complete.
125 bytes sent in 00:00 (2.71 KiB/s)
ftp> cd site/deployments/tools
250 CWD command successful.
ftp> put mssql-datasource-commands.cli
local: mssql-datasource-commands.cli remote: mssql-datasource-commands.cli
229 Entering Extended Passive Mode (|||10200|)
125 Data connection already open; Transfer starting.
100% |*******************************************************|  1751      301.42 KiB/s    --:-- ETA
226 Transfer complete.
1751 bytes sent in 00:00 (35.69 KiB/s)
ftp> put mssql-module.xml
local: mssql-module.xml remote: mssql-module.xml
229 Entering Extended Passive Mode (|||10201|)
125 Data connection already open; Transfer starting.
100% |*******************************************************|   305       51.39 KiB/s    --:-- ETA
226 Transfer complete.
305 bytes sent in 00:00 (7.07 KiB/s)
ftp> bin
200 Type set to I.
ftp> put mssql-jdbc-7.2.1.jre8.jar
local: mssql-jdbc-7.2.1.jre8.jar remote: mssql-jdbc-7.2.1.jre8.jar
229 Entering Extended Passive Mode (|||10202|)
125 Data connection already open; Transfer starting.
100% |*******************************************************|  1135 KiB    1.62 MiB/s    00:00 ETA
226 Transfer complete.
1162710 bytes sent in 00:00 (1.48 MiB/s)
ftp> bye
221 Goodbye.
Step 3: Set SQL Database connection info in the Web app environment

Use Azure CLI to set database connection info:

az webapp config appsettings set  \
    --resource-group ${RESOURCEGROUP_NAME} \
    --name ${WEBAPP_NAME}  \
    --settings  SQL_CONNECTION_URL=${SQL_CONNECTION_URL}

[
  {
    "name": "WEBSITE_HTTPLOGGING_RETENTION_DAYS",
    "slotSetting": false,
    "value": "3"
  },
  {
    "name": "SQL_CONNECTION_URL",
    "slotSetting": false,
    "value": "======= MASKED ======="
  }
]
Step 4: Test the JBoss/WildFly CLI commands to configure data source

You can test Bash script for configuring data source by running them on App Service Linux by opening an SSH connection from your development machine:

# ======== first terminal window =========
az webapp remote-connection create --resource-group ${RESOURCEGROUP_NAME} --name ${WEBAPP_NAME} &
[1] 10851
bash-3.2$ Auto-selecting port: 54155
SSH is available { username: root, password: Docker! }
Start your favorite client and connect to port 54155
Websocket tracing disabled, use --verbose flag to enable
Successfully connected to local server..

ssh root@localhost -p 58386
The authenticity of host '[localhost]:58386 ([127.0.0.1]:58386)' can't be established.
ECDSA key fingerprint is SHA256:7EHlhnWmPC600borWmiBMP43SdXIHedlk4sKIfJKu3I.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:58386' (ECDSA) to the list of known hosts.
root@localhost's password:
  _____
  /  _  \ __________ _________   ____
 /  /_\  \___   /  |  \_  __ \_/ __ \
/    |    \/    /|  |  /|  | \/\  ___/
\____|__  /_____ \____/ |__|    \___  >
        \/      \/                  \/
A P P   S E R V I C E   O N   L I N U X

Documentation: http://aka.ms/webapp-linux
ee72972be508:/home#

# ======== run JBoss/WildFly CLI commands to configure a data source ===========

ee72972be508:/home# source startup.sh
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true
"Configuring sqlDS ==================="
"Installing MSSQL module"
"Installing MSSQL driver"
"Installing MSSQL datasource"
The batch executed successfully
ee72972be508:/home# /opt/jboss/wildfly/bin/jboss-cli.sh -c
Picked up _JAVA_OPTIONS: -Djava.net.preferIPv4Stack=true

[standalone@localhost:9990 /] /subsystem=datasources/data-source=sqlDS:test-connection-in-pool()
{
    "outcome" => "success",
    "result" => [true]
}

[standalone@localhost:9990 /] exit
Step 5: Restart the remote WildFly app server

Use Azure CLI to restart the remote WildFly app server:

az webapp stop -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}
az webapp start -g ${RESOURCEGROUP_NAME} -n ${WEBAPP_NAME}

For additional info, please refer to:

Build PetStore to interact with Azure SQL Database

# Use the Maven profile for Azure SQL Database to build

mvn package -Dmaven.test.skip=true -Ddb=sql
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- copy-rename-maven-plugin:1.0:copy (copy-file) @ petstoreee7 ---
[INFO] Copied /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence-sql.xml to /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7/src/main/resources/META-INF/persistence.xml
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ petstoreee7 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 14 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ petstoreee7 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- swagger-maven-plugin:3.1.6:generate (default) @ petstoreee7 ---
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ petstoreee7 ---
[INFO] Not copying test resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ petstoreee7 ---
[INFO] Not compiling test sources
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ petstoreee7 ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- maven-war-plugin:3.1.0:war (default-war) @ petstoreee7 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [petstoreee7] in [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7/target/applicationPetstore]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7/src/main/webapp]
[INFO] Webapp assembled in [394 msecs]
[INFO] Building war: /Users/selvasingh/migrate-Java-EE-app-to-azure/initial-sql/agoncal-application-petstore-ee7/target/applicationPetstore.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.850 s
[INFO] Finished at: 2019-06-01T17:47:53-07:00
[INFO] Final Memory: 22M/338M
[INFO] ------------------------------------------------------------------------

Deploy to App Service Linux

Deploy to WildFly in App Service Linux:

mvn azure-webapp:deploy
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Petstore application using Java EE 7 7.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- azure-webapp-maven-plugin:1.6.0:deploy (default-cli) @ petstoreee7 ---
[INFO] Authenticate with Azure CLI 2.0
[INFO] Updating target Web App...
[INFO] Successfully updated Web App.
[INFO] Trying to deploy artifact to petstore-java-ee...
[INFO] Deploying the war file...
[INFO] Successfully deployed the artifact to https://petstore-java-ee.azurewebsites.net
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:25 min
[INFO] Finished at: 2019-06-01T17:54:14-07:00
[INFO] Final Memory: 50M/635M
[INFO] ------------------------------------------------------------------------

Open Pet Store running on App Service Linux and interacting with Azure SQL Database

open https://petstore-java-ee.azurewebsites.net

Log into Azure SQL Database and Validate Tables were Created and Populated

# Show tables in SQL Database
sqlcmd -S ${SQL_SERVER_FULL_NAME} \
    -d ${SQL_DATABASE_NAME} \
    -U ${SQL_SERVER_ADMIN_FULL_NAME} \
    -P ${SQL_SERVER_ADMIN_PASSWORD} \
    -Q "SELECT name, id, crdate FROM SYSOBJECTS WHERE xtype = 'U'"

--------------------- ----------- -----------------------
Category                 18099105 2019-05-31 14:41:52.280
Country                  50099219 2019-05-31 14:41:52.320
Customer                 82099333 2019-05-31 14:41:52.330
Item                    114099447 2019-05-31 14:41:52.347
order_line              146099561 2019-05-31 14:41:52.373
Product                 194099732 2019-05-31 14:41:52.390
purchase_order          226099846 2019-05-31 14:41:52.427
t_order_order_line      258099960 2019-05-31 14:41:52.483

(8 rows affected)

# Show contents in category table

sqlcmd -S ${SQL_SERVER_FULL_NAME} \
    -d ${SQL_DATABASE_NAME} \
    -U ${SQL_SERVER_ADMIN_FULL_NAME} \
    -P ${SQL_SERVER_ADMIN_PASSWORD} \
    -Q "select name from category"

name                          
------------------------------
Fish                          
Dogs                          
Reptiles                      
Cats                          
Birds                         

(5 rows affected)

Trouble Shoot Petstore on Azure by Viewing Logs

Open Java Web app remote log stream from a local machine:

az webapp log tail --name ${WEBAPP_NAME} \
 --resource-group ${RESOURCEGROUP_NAME}

2018-12-22T00:47:48  Welcome, you are now connected to log-streaming service.
2018-12-22T00:41:45.064280703Z   _____                               
2018-12-22T00:41:45.064325203Z   /  _  \ __________ _________   ____  
2018-12-22T00:41:45.064331403Z  /  /_\  \___   /  |  \_  __ \_/ __ \ 
2018-12-22T00:41:45.064335603Z /    |    \/    /|  |  /|  | \/\  ___/ 
2018-12-22T00:41:45.064339403Z \____|__  /_____ \____/ |__|    \___  >
2018-12-22T00:41:45.064343503Z         \/      \/                  \/ 
2018-12-22T00:41:45.064347403Z A P P   S E R V I C E   O N   L I N U X  
...
...

2019-06-02T00:55:35.520469647Z 00:55:35,520 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) started in 9816ms - Started 81 of 93 services (31 services are lazy, passive or on-demand)
2019-06-02T00:55:38.548395649Z ***Admin server is ready
2019-06-02T00:55:38.555859867Z STARTUP_FILE=/home/startup.sh
2019-06-02T00:55:38.582558031Z STARTUP_COMMAND=
2019-06-02T00:55:38.587032142Z Copying /home/startup.sh to /tmp/startup.sh and fixing EOL characters in /tmp/startup.sh
2019-06-02T00:55:38.593801458Z Running STARTUP_FILE: /tmp/startup.sh
2019-06-02T00:55:38.628501142Z Picked up JAVA_TOOL_OPTIONS:  -Djava.net.preferIPv4Stack=true
2019-06-02T00:55:42.206161168Z "Configuring sqlDS ==================="
2019-06-02T00:55:42.211727982Z "Installing MSSQL module"
2019-06-02T00:55:42.253643983Z "Installing MSSQL driver"
2019-06-02T00:55:42.284713558Z "Installing MSSQL datasource"
2019-06-02T00:55:42.490971355Z The batch executed successfully
2019-06-02T00:55:42.552288503Z 00:55:42,552 INFO  [org.jboss.as] (MSC service thread 1-1) WFLYSRV0050: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) stopped in 47ms
2019-06-02T00:55:42.564687033Z 00:55:42,564 INFO  [org.jboss.as] (MSC service thread 1-2) WFLYSRV0049: WildFly Full 14.0.1.Final (WildFly Core 6.0.2.Final) starting
...
...
2019-06-02T00:55:44.293778902Z 00:55:44,293 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 44) WFLYJCA0004: Deploying JDBC-compliant driver class com.microsoft.sqlserver.jdbc.SQLServerDriver (version 7.2)
2019-06-02T00:55:44.594576027Z 00:55:44,594 INFO  [org.jboss.as.connector.deployers.jdbc] (MSC service thread 1-2) WFLYJCA0018: Started Driver service with driver-name = sqlserver
2019-06-02T00:55:44.607447158Z 00:55:44,601 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) WFLYJCA0010: Unbound data source [java:jboss/datasources/ExampleDS]
2019-06-02T00:55:44.610591266Z 00:55:44,610 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) WFLYJCA0010: Unbound data source [java:jboss/datasources/sqlDS]
...
...
019-06-02T00:55:56.153951398Z 00:55:56,152 INFO  [org.jboss.as.jpa] (ServerService Thread Pool -- 79) WFLYJPA0010: Starting Persistence Unit (phase 2 of 2) Service 'ROOT.war#applicationPetstorePU'
2019-06-02T00:55:57.237113910Z 00:55:57,232 INFO  [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 79) HHH000400: Using dialect: org.hibernate.dialect.SQLServer2012Dialect

When you are finished, you can check your results against YOUR code in migrate-Java-EE-app-to-azure/complete.

Scale out the Pet Store app

Scale out Java Web app using Azure CLI:

az appservice plan update --number-of-workers 2 \
   --name ${WEBAPP_PLAN_NAME} \
   --resource-group ${RESOURCEGROUP_NAME}

Congratulations!

Congratulations!! You migrated an existing Java EE workload to Azure, aka app to App Service Linux and app's data to Azure Database for PostgreSQL, MySQL and or SQL Database.

Resources


This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

About

Migrate an existing Java EE workload to Azure

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages