Tutorial: Build a JBoss web app with Azure App Service on Linux and MySQL

This tutorial shows how to build, configure, and deploy a secure JBoss application in Azure App Service that connects to a MySQL database (using Azure Database for MySQL). Azure App Service is a highly scalable, self-patching, web-hosting service that can easily deploy apps on Windows or Linux. When you're finished, you'll have a JBoss app running on Azure App Service on Linux.

Screenshot of JBoss application storing data in MySQL.

In this tutorial, you learn how to:

  • Create a secure-by-default architecture for Azure App Service and Azure Database for MySQL flexible server.
  • Secure database connectivity using a passwordless connection string.
  • Verify JBoss data sources in App Service using JBoss CLI.
  • Deploy a JBoss sample app to App Service from a GitHub repository.
  • Acces App Service app settings in the application code.
  • Make updates and redeploy the application code.
  • Stream diagnostic logs from App Service.
  • Manage the app in the Azure portal.

Prerequisites

  • An Azure account with an active subscription. If you don't have an Azure account, you can create one for free.
  • A GitHub account. you can also get one for free.
  • Knowledge of Java with JBoss development.

1. Run the sample

First, you set up a sample data-driven app as a starting point. For your convenience, the sample repository, includes a dev container configuration. The dev container has everything you need to develop an application, including the database, cache, and all environment variables needed by the sample application. The dev container can run in a GitHub codespace, which means you can run the sample on any computer with a web browser.

Step 1: In a new browser window:

  1. Sign in to your GitHub account.
  2. Navigate to https://github.com/Azure-Samples/msdocs-jboss-mysql-sample-app/fork.
  3. Select Create fork.

Step 2: In the GitHub fork:

Select Code > Create codespace on main. The codespace takes a few minutes to set up.

Step 3: In the codespace terminal:

  1. Run mvn clean wildfly:run.
  2. When you see the notification Your application running on port 8080 is available., wait a few seconds longer for the WildFly server to finish loading the application. Then, select Open in Browser. You should see the sample application in a new browser tab. To stop the WildFly server, type Ctrl+C.

Tip

You can ask GitHub Copilot about this repository. For example:

  • @workspace What does this project do?
  • @workspace What does the .devcontainer folder do?

Having issues? Check the Troubleshooting section.

2. Create App Service and MySQL

First, you create the Azure resources. The steps used in this tutorial create a set of secure-by-default resources that include App Service and Azure Database for MySQL. For the creation process, you specify:

  • The Name for the web app. It's used as part of the DNS name for your app in the form of https://<app-name>-<hash>.<region>.azurewebsites.net.
  • The Region to run the app physically in the world. It's also used as part of the DNS name for your app.
  • The Runtime stack for the app. It's where you select the version of Java to use for your app.
  • The Hosting plan for the app. It's the pricing tier that includes the set of features and scaling capacity for your app.
  • The Resource Group for the app. A resource group lets you group (in a logical container) all the Azure resources needed for the application.

Sign in to the Azure portal and follow these steps to create your Azure App Service resources.

Step 1: In the Azure portal:

  1. In the top search bar, type app service.
  2. Select the item labeled App Service under the Services heading.
  3. Select Create > Web App. You can also navigate to the creation wizard directly.

Step 2: In the Create Web App page, fill out the form as follows.

  1. Name: msdocs-jboss-mysql. A resource group named msdocs-jboss-mysql_group will be generated for you.
  2. Runtime stack: Java 17.
  3. Java web server stack: Red Hat JBoss EAP 8. If you configured your Red Hat subscription with Azure already, select Red Hat JBoss EAP 8 BYO License.
  4. Region: Any Azure region near you.
  5. Linux Plan: Create new and use the name msdocs-jboss-mysql.
  6. Pricing plan: Premium V3 P0V3. When you're ready, you can scale up to a different pricing tier.
  7. Deploy with your app: Select Database. Azure Database for MySQL - Flexible Server is selected for you by default. It's a fully managed MySQL database as a service on Azure, compatible with the latest community editions.
  8. Select Review + create.
  9. After validation completes, select Create.

Step 3: The deployment takes a few minutes to complete. Once deployment completes, select the Go to resource button. You're taken directly to the App Service app, but the following resources are created:

  • Resource group: The container for all the created resources.
  • App Service plan: Defines the compute resources for App Service. A Linux plan in the Basic tier is created.
  • App Service: Represents your app and runs in the App Service plan.
  • Virtual network: Integrated with the App Service app and isolates back-end network traffic.
  • Azure Database for MySQL flexible server: Accessible only from the virtual network. A database and a user are created for you on the server.
  • Private DNS zones: Enable DNS resolution of the database server in the virtual network.

Having issues? Check the Troubleshooting section.

3. Create a passwordless connection

In this step, you generate a managed identity based service connection, which you can later use to create a data source in your JBoss server. By using a managed identity to connect to the MySQL database, your code is safe from accidental secrets leakage.

Step 1: Create a managed identity.

  1. In the top search bar, type managed identity.
  2. Select the item labeled Managed Identities under the Services heading.
  3. Select Create.
  4. In Resource group, select msdocs-jboss-mysql_group.
  5. In Region, select the same region that you used for your web app.
  6. In Name, type msdocs-jboss-mysql-server-identity.
  7. Select Review + create.
  8. Select Create.

Step 2: Enable Microsoft Entra authentication in the MySQL server.

  1. In the top search bar, type msdocs-jboss-mysql-server.
  2. Select the Azure Database for MySQL flexible server resource called msdocs-jboss-mysql-server.
  3. From the left menu, select Security > Authentication.
  4. In Assign access to, select Microsoft Entra authentication only.
  5. In User assigned managed identity, select Select.
  6. Select msdocs-jboss-mysql-server-identity, then select Add. It takes a moment for the identity to be assigned to the MySQL server.
  7. In Microsoft Entra Admin Name, select Select.
  8. Find your Azure account and select it, then select Select.
  9. Select Save and wait for the operation to complete.

Step 3: Add a managed identity-based service connector.

  1. In the top search bar, type msdocs-jboss-mysql.
  2. Select the App Service resource called msdocs-jboss-mysql.
  3. In the App Service page, in the left menu, select Settings > Service Connector.
  4. Select Create.
  5. In the Basics tab:
  6. Set Service type to DB for MySQL flexible server.
  7. Set MySQL flexible server to msdocs-jboss-mysql-server.
  8. Set MySQL database to msdocs-jboss-mysql-database.
  9. Set Client type to Java.
  10. Select the Authentication tab.
  11. Select System assigned managed identity.
  12. Select the Review + Create tab.
  13. When validation completes, select Create on Cloud Shell and wait for the operation to complete in the Cloud Shell.
  14. When you see the output JSON, you can close the Cloud Shell. Also, close the Create connection dialog.
  15. Select Refresh to show the new service connector.

Step 4: Add authentication plugins to the connection string.

  1. From the left menu, select Environment variables > Connection strings.
  2. Select AZURE_MYSQL_CONNECTIONSTRING. The Value field should contain a user but no password. The user is a managed identity.
  3. The JBoss server in your App Service app has the authentication plugins authenticate the managed identity, but you still need to add it to the connection string. Scroll to the end of the value and append &defaultAuthenticationPlugin=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin&authenticationPlugins=com.azure.identity.extensions.jdbc.mysql.AzureMysqlAuthenticationPlugin.
  4. Select Apply.
  5. Select Apply, then Confirm.

Having issues? Check the Troubleshooting section.

4. Confirm JNDI data source

If you add an app setting that contains a valid JDBC connection string for Oracle, SQL Server, PostgreSQL, or MySQL, App Service adds a Java Naming and Directory Interface (JNDI) data source for it in the JBoss server. In this step, you use the SSH connection to the app container to verify the JNDI data source. In the process, you learn how to access the SSH shell and run the JBoss CLI.

Step 1: Back in the App Service page:

  1. In the left menu, select Development Tools > SSH.
  2. Select Go.

Step 2: In the SSH terminal:

  1. Run $JBOSS_HOME/bin/jboss-cli.sh --connect.
  2. In the JBoss CLI connection, run ls subsystem=datasources/data-source. You should see the automatically generated data source called AZURE_MYSQL_CONNECTIONSTRING_DS.
  3. Get the JNDI name of the data source with /subsystem=datasources/data-source=AZURE_MYSQL_CONNECTIONSTRING_DS:read-attribute(name=jndi-name). You now have a JNDI name java:jboss/env/jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS, which you can use in your application code later.

Note

Only changes to files in /home can persist beyond app restarts. For example, if you edit /opt/eap/standalone/configuration/standalone.xml or change server configuration in the JBoss CLI, the changes won't persist beyond an app restart. To persist your changes, use a startup script, such as demonstrated in Configure data sources for a Tomcat, JBoss, or Java SE app in Azure App Service

Having issues? Check the Troubleshooting section.

5. Deploy sample code

In this step, you configure GitHub deployment using GitHub Actions. It's just one of many ways to deploy to App Service, but also a great way to have continuous integration in your deployment process. By default, every git push to your GitHub repository kicks off the build and deploy action.

Like the JBoss convention, if you want to deploy to the root context of JBoss, name your built artifact ROOT.war.

Step 1: Back in the App Service page, in the left menu, select Deployment > Deployment Center.

Step 2: In the Deployment Center page:

  1. In Source, select GitHub. By default, GitHub Actions is selected as the build provider.
  2. Sign in to your GitHub account and follow the prompt to authorize Azure.
  3. In Organization, select your account.
  4. In Repository, select msdocs-jboss-mysql-sample-app.
  5. In Branch, select main. This is the same branch that you worked in with your sample app, without any Azure-related files or configuration.
  6. For Authentication type, select User-assigned identity.
  7. In the top menu, select Save. App Service commits a workflow file into the chosen GitHub repository, in the .github/workflows directory. By default, the deployment center creates a user-assigned identity for the workflow to authenticate using Microsoft Entra (OIDC authentication). For alternative authentication options, see Deploy to App Service using GitHub Actions.

Step 3: Back in the GitHub codespace of your sample fork, run git pull origin main. This pulls the newly committed workflow file into your codespace. You can modify it according to your needs at .github/workflows/main_msdocs-jboss-mysql.yml.

Step 4 (Option 1: with GitHub Copilot):

  1. Start a new chat session by clicking the Chat view, then clicking +.
  2. Ask, "@workspace How does the app connect to the database?" Copilot might give you some explanation about the java:jboss/MySQLDS data source and how it's configured.
  3. Say, "The data source in JBoss in Azure uses the JNDI name java:jboss/env/jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS." Copilot might give you a code suggestion similar to the one in the Option 2: without GitHub Copilot steps below and even tell you to make the change in the class. GitHub Copilot doesn't give you the same response every time, you might need to ask more questions to fine-tune its response. For tips, see What can I do with GitHub Copilot in my codespace?.

Step 4 (Option 2: without GitHub Copilot):

  1. Open src/main/resources/META-INF/persistence.xml in the explorer. When the application starts, it loads the database settings in this file.
  2. Change the value of <jta-data-source> to java:jboss/env/jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS, which is the data source you found with JBoss CLI earlier in the SSH shell.

Step 5:

  1. Select the Source Control extension.
  2. In the textbox, type a commit message like Configure Azure JNDI name.
  3. Select Commit, then confirm with Yes.
  4. Select Sync changes 1, then confirm with OK.

Step 6: Back in the Deployment Center page in the Azure portal:

  1. Select Logs. A new deployment run is already started from your committed changes.
  2. In the log item for the deployment run, select the Build/Deploy Logs entry with the latest timestamp.

Step 7: You're taken to your GitHub repository and see that the GitHub action is running. The workflow file defines two separate stages, build and deploy. Wait for the GitHub run to show a status of Complete. It takes about 5 minutes.

Having issues? Check the Troubleshooting section.

6. Browse to the app

Step 1: In the App Service page:

  1. From the left menu, select Overview.
  2. In Default domain, select the URL of your app.

Step 2: Add a few tasks to the list. Congratulations, you're running a web app in Azure App Service, with secure connectivity to Azure Database for MySQL.

Having issues? Check the Troubleshooting section.

7. Stream diagnostic logs

Azure App Service captures all messages output to the console to help you diagnose issues with your application. The sample application includes standard Log4j logging statements to demonstrate this capability, as shown in the following snippet:

private static final Logger logger = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

@PersistenceContext
private EntityManager entityManager;

public List<Task> getAllTasks() {
    logger.log(Level.INFO, "Finding all tasks. ");

    return this.entityManager.createNamedQuery("findAllTasks", Task.class).getResultList();
}

In the App Service page, from the left menu, select Log stream. You see the logs for your app, including platform logs and logs from inside the container.

Learn more about logging in Java apps in the series on Enable Azure Monitor OpenTelemetry for .NET, Node.js, Python and Java applications.

Having issues? Check the Troubleshooting section.

8. Clean up resources

When you're finished, you can delete all of the resources from your Azure subscription by deleting the resource group.

Step 1: In the search bar at the top of the Azure portal:

  1. Enter the resource group name msdocs-jboss-mysql_group.
  2. Select the resource group.

Step 2: In the resource group page, select Delete resource group.

Step 3:

  1. Confirm your deletion by typing the resource group name.
  2. Select Delete.
  3. Confirm with Delete again.

Troubleshooting

I see the error 'not entitled to use the Bring Your Own License feature' in the creation wizard.

If you see the error: The subscription '701ea799-fb46-4407-bb67-9cbcf289f1c7' is not entitled to use the Bring Your Own License feature when creating the application, it means that you selected Red Hat JBoss EAP 7/8 BYO License in Java web server stack but haven't set up your Azure account in Red Hat Cloud Access or don't have an active JBoss EAP license in Red Hat Cloud Access.

The portal deployment view for Azure Database for MySQL Flexible Server shows a Conflict status.

Depending on your subscription and the region you select, you might see the deployment status for Azure Database for MySQL Flexible Server to be Conflict, with the following message in Operation details:

InternalServerError: An unexpected error occured while processing the request.

This error is most likely caused by a limit on your subscription for the region you select. Try choosing a different region for your deployment.

The Create connection dialog shows a Create On Cloud Shell button but it's not enabled.

You might also see an error message in the dialog: The database server is in Virtual Network and Cloud Shell can't connect to it. Please copy the commands and execute on an environment which can connect to the database server in Virtual Network.

The service connector automation needs network access to the MySQL server. Look in the networking settings of your MySQL server resource and make sure Allow public access to this resource through the internet using a public IP address is selected at a minimum. Service Connector can take it from there.

If you don't see this checkbox, you might have created the deployment using the Web App + Database wizard instead, and the deployment locks down all public network access to the MySQL server. There's no way to modify the configuration. Since app's Linux container can access MySQL through the virtual network integration, you could install Azure CLI in the app's SSH session and run the supplied Cloud Shell commands there.

The deployed sample app doesn't show the tasks list app.

If you see the JBoss splash page instead of the tasks list app, App Service is most likely still loading the updated container from your most recent code deployment. Wait a few minutes and refresh the page.

My app failed to start, and I see 'Access denied for user... (using password: NO)' in the logs.

This error is most likely because you didn't add the passwordless authentication plugin to the connection string (see the Java sample code for Integrate Azure Database for MySQL with Service Connector). Change the MySQL connection string by following the instructions in 3. Create a passwordless connection.

I see a "Table 'Task' already exists" error in the diagnostic logs.

You can ignore this Hibernate error because it indicates that the application code is connected to the MySQL database. The application is configured to create the necessary tables when it starts (see src/main/resources/META-INF/persistence.xml). When the application starts the first time, it should create the tables successfully, but on subsequent restarts, you would see this error because the tables already exist.

Frequently asked questions

How much does this setup cost?

Pricing for the created resources is as follows:

How do I connect to the MySQL server behind the virtual network with other tools?

  • The JBoss container currently doesn't have the mysql-client terminal too. If you want, you must manually install it. Remember that anything you install doesn't persist across app restarts.
  • To connect from a desktop tool like MySQL Workbench, your machine must be within the virtual network. For example, it could be an Azure VM in one of the subnets, or a machine in an on-premises network that has a site-to-site VPN connection with the Azure virtual network.
  • You can also integrate Azure Cloud Shell with the virtual network.

How does local app development work with GitHub Actions?

Using the autogenerated workflow file from App Service as an example, each git push kicks off a new build and deployment run. From a local clone of the GitHub repository, you make the desired updates and push to GitHub. For example:

git add .
git commit -m "<some-message>"
git push origin main

I don't have permissions to create a user-assigned identity

See Set up GitHub Actions deployment from the Deployment Center.

What can I do with GitHub Copilot in my codespace?

You might notice that the GitHub Copilot chat view was already there for you when you created the codespace. For your convenience, we include the GitHub Copilot chat extension in the container definition (see .devcontainer/devcontainer.json). However, you need a GitHub Copilot account (30-day free trial available).

A few tips for you when you talk to GitHub Copilot:

  • In a single chat session, the questions and answers build on each other and you can adjust your questions to fine-tune the answer you get.
  • By default, GitHub Copilot doesn't have access to any file in your repository. To ask questions about a file, open the file in the editor first.
  • To let GitHub Copilot have access to all of the files in the repository when preparing its answers, begin your question with @workspace. For more information, see Use the @workspace agent.
  • In the chat session, GitHub Copilot can suggest changes and (with @workspace) even where to make the changes, but it's not allowed to make the changes for you. It's up to you to add the suggested changes and test it.

Here are some other things you can say to fine-tune the answer you get:

  • Change this code to use the data source jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS.
  • Some imports in your code are using javax but I have a Jakarta app.
  • I want this code to run only if the environment variable AZURE_MYSQL_CONNECTIONSTRING is set.
  • I want this code to run only in Azure App Service and not locally.

Next steps

Learn more about running Java apps on App Service in the developer guide.

Learn how to secure your app with a custom domain and certificate.