Events
Mar 17, 9 PM - Mar 21, 10 AM
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register nowThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
This tutorial shows how to build, configure, and deploy a secure Tomcat 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 have a Tomcat app running on Azure App Service on Linux.
In this tutorial, you learn how to:
You can quickly deploy the sample app in this tutorial and see it running in Azure. Just run the following commands in the Azure Cloud Shell, and follow the prompt:
mkdir msdocs-tomcat-mysql-sample-app
cd msdocs-tomcat-mysql-sample-app
azd init --template msdocs-tomcat-mysql-sample-app
azd up
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:
Step 2: In the GitHub fork:
Step 3: In the codespace terminal:
mvn jetty:run
.Your application running on port 80 is available.
, select Open in Browser.
You should see the sample application in a new browser tab.
To stop the Jetty server, type Ctrl
+C
.Tip
You can ask GitHub Copilot about this repository. For example:
Having issues? Check the Troubleshooting section.
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:
https://<app-name>-<hash>.<region>.azurewebsites.net
.Sign in to the Azure portal and follow these steps to create your Azure App Service resources.
Step 1: In the Azure portal:
Step 2: In the Create Web App + Database page, fill out the form as follows.
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:
Having issues? Check the Troubleshooting section.
The creation wizard generated the database connectivity string for you already as an app setting. However, the security best practice is to keep secrets out of App Service completely. You move your secrets to a key vault and change your app setting to a Key Vault reference with the help of Service Connectors.
Step 1: Retrieve the existing connection string
Step 2: Create a key vault for secure management of secrets
Step 3: Secure the key vault with a Private Endpoint
Step 4: Configure the Service Connector
Step 5: Establish the Key Vault connection
Step 6: Finalize the Service Connector configuration
Step 7: Verify the Key Vault integration
@Microsoft.KeyVault(...)
, which means that it's a key vault reference because the secret is now managed in the key vault.To summarize, the process involved retrieving the MySQL connection string from the App Service's environment variables, creating an Azure Key Vault for secure secret management with private access, and updating the service connector to store the password in the key vault. A secure connection between the App Service app and key vault was established using a system-assigned managed identity, and the setup was verified by confirming the connection string uses a Key Vault reference.
Having issues? Check the Troubleshooting section.
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 Tomcat server's context.xml file. 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 for the Tomcat container.
Step 1: Back in the App Service page:
Step 2: In the SSH terminal, run cat /usr/local/tomcat/conf/context.xml
. You should see that a JNDI resource called jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS
was added. You use this data source later.
Note
Only changes to files in /home
can persist beyond app restarts. For example, if you edit /usr/local/tomcat/conf/server.xml
, the changes won't persist beyond an app restart.
Having issues? Check the Troubleshooting section.
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 Tomcat convention, if you want to deploy to the root context of Tomcat, name your built artifact ROOT.war.
Step 1: Back in the App Service page, in the left menu, select Deployment Center.
Step 2: In the Deployment Center page:
.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 starter-no-infra
.
This pulls the newly committed workflow file into your codespace.
Step 4 (Option 1: with GitHub Copilot):
jdbc/MYSQLDS
data source and how it's configured.contextInitialized
method.
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):
contextIntialized()
method, find the commented code (lines 29-33) and uncomment it.
This code checks to see if the AZURE_MYSQL_CONNECTIONSTRING
app setting exists, and changes the data source to java:comp/env/jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS
, which is the data source you found earlier in context.xml in the SSH shell.Step 5:
Configure Azure data source
.Step 6: Back in the Deployment Center page in the Azure portal:
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.
Step 1: In the App Service page:
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.
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:
@WebServlet(urlPatterns = "/")
public class ViewServlet extends HttpServlet {
private static Logger logger = LogManager.getLogger(ViewServlet.class.getName());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("GET /");
EntityManagerFactory emf = (EntityManagerFactory) req.getServletContext().getAttribute("EMFactory");
Step 1: In the App Service page:
Step 2: 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.
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:
Step 2: In the resource group page, select Delete resource group.
Step 3:
In this step, you create the Azure resources and deploy a sample app to App Service on Linux. The steps used in this tutorial create a set of secure-by-default resources that include App Service and Azure Database for MySQL.
The dev container already has the Azure Developer CLI (AZD).
From the repository root, run azd init
.
azd init --template tomcat-app-service-mysql-infra
When prompted, give the following answers:
Question | Answer |
---|---|
The current directory is not empty. Would you like to initialize a project here in '<your-directory>'? | Y |
What would you like to do with these files? | Keep my existing files unchanged |
Enter a new environment name | Type a unique name. The AZD template uses this name as part of the DNS name of your web app in Azure (<app-name>-<hash>.azurewebsites.net ). Alphanumeric characters and hyphens are allowed. |
Sign into Azure by running the azd auth login
command and following the prompt:
azd auth login
Create the necessary Azure resources and deploy the app code with the azd up
command. Follow the prompt to select the desired subscription and location for the Azure resources.
azd up
The azd up
command takes about 15 minutes to complete (the Redis cache takes the most time). It also compiles and deploys your application code, but you modify your code later to work with App Service. While it's running, the command provides messages about the provisioning and deployment process, including a link to the deployment in Azure. When it finishes, the command also displays a link to the deploy application.
This AZD template contains files (azure.yaml and the infra directory) that generate a secure-by-default architecture with the following Azure resources:
Once the command finishes creating resources and deploying the application code the first time, the deployed sample app doesn't work yet because you must make small changes to make it connect to the database in Azure.
Having issues? Check the Troubleshooting section.
The AZD template you use generated the connectivity variables for you already as app settings and outputs the them to the terminal for your convenience. App settings are one way to keep connection secrets out of your code repository.
In the AZD output, find the app setting AZURE_MYSQL_CONNECTIONSTRING
. Only the setting names are displayed. They look like this in the AZD output:
App Service app has the following connection strings: - AZURE_MYSQL_CONNECTIONSTRING - AZURE_REDIS_CONNECTIONSTRING - AZURE_KEYVAULT_RESOURCEENDPOINT - AZURE_KEYVAULT_SCOPE
AZURE_MYSQL_CONNECTIONSTRING
contains the connection string to the MySQL database in Azure. You need to use it in your code later.
For your convenience, the AZD template shows you the direct link to the app's app settings page. Find the link and open it in a new browser tab.
If you add an app setting that contains a valid Oracle, SQL Server, PostgreSQL, or MySQL connection string, App Service adds it as a Java Naming and Directory Interface (JNDI) data source in the Tomcat server's context.xml file.
Having issues? Check the Troubleshooting section.
In this step, you use the SSH connection to the app container to verify the JNDI data source in the Tomcat server. In the process, you learn how to access the SSH shell for the Tomcat container.
In the AZD output, find the URL for the SSH session and navigate to it in the browser. It looks like this in the output:
Open SSH session to App Service container at: https://<app-name>-<hash>.scm.azurewebsites.net/webssh/host
In the SSH terminal, run cat /usr/local/tomcat/conf/context.xml
. You should see that a JNDI resource called jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS
was added. You use this data source later.
Note
Only changes to files in /home
can persist beyond app restarts. For example, if you edit /usr/local/tomcat/conf/server.xml
, the changes won't persist beyond an app restart.
Having issues? Check the Troubleshooting section.
In the GitHub codespace, start a new chat session by clicking the Chat view, then clicking +.
Ask, "@workspace How does the app connect to the database?" Copilot might give you some explanation about the jdbc/MYSQLDS
data source and how it's configured.
Ask, "@workspace I want to replace the data source defined in persistence.xml with an existing JNDI data source in Tomcat but I want to do it dynamically." 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 ContextListener class.
Open src/main/java/com/microsoft/azure/appservice/examples/tomcatmysql/ContextListener.java in the explorer and add the code suggestion in the contextInitialized
method.
GitHub Copilot doesn't give you the same response every time, you might need to ask other questions to fine-tune its response. For tips, see What can I do with GitHub Copilot in my codespace?.
In the codespace terminal, run azd deploy
.
azd deploy
Tip
You can also just use azd up
always, which does all of azd package
, azd provision
, and azd deploy
.
To find out how the War file is packaged, you can run azd package --debug
by itself.
Having issues? Check the Troubleshooting section.
In the AZD output, find the URL of your app and navigate to it in the browser. The URL looks like this in the AZD output:
Deploying services (azd deploy) (✓) Done: Deploying service web - Endpoint: https://<app-name>-<hash>.azurewebsites.net/
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.
Azure App Service can capture console logs to help you diagnose issues with your application. For convenience, the AZD template already enabled logging to the local file system and is shipping the logs to a Log Analytics workspace.
The sample application includes standard Log4j logging statements to demonstrate this capability, as shown in the following snippet:
@WebServlet(urlPatterns = "/")
public class ViewServlet extends HttpServlet {
private static Logger logger = LogManager.getLogger(ViewServlet.class.getName());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("GET /");
EntityManagerFactory emf = (EntityManagerFactory) req.getServletContext().getAttribute("EMFactory");
In the AZD output, find the link to stream App Service logs and navigate to it in the browser. The link looks like this in the AZD output:
Stream App Service logs at: https://portal.azure.com/#@/resource/subscriptions/<subscription-guid>/resourceGroups/<group-name>/providers/Microsoft.Web/sites/<app-name>/logStream
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.
To delete all Azure resources in the current deployment environment, run azd down
and follow the prompts.
azd down
You can ignore the warnings. The Maven Jetty plugin shows the warnings because the app's pom.xml contains the dependency for jakarta.servlet.jsp.jstl
, which the Jetty already provides out of the box. You need the dependency for Tomcat.
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.
If you see a Hey, Java developers!
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.
Make sure that you made the code changes to use the java:comp/env/jdbc/AZURE_MYSQL_CONNECTIONSTRING_DS
data source. If you made the changes and redeployed your code, App Service is most likely still loading the updated container. Wait a few minutes and refresh the page.
Pricing for the created resources is as follows:
mysql-client
terminal too. If you want, you must manually install it. Remember that anything you install doesn't persist across app restarts.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
See Set up GitHub Actions deployment from the Deployment Center.
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:
@workspace
. For more information, see Use the @workspace agent.@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:
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.
Events
Mar 17, 9 PM - Mar 21, 10 AM
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register nowTraining
Learning path
Best practices for Java apps on Azure - Training
Start here and learn how you can monitor, automate, tune, autoscale, secure and build Java apps on Azure. As always, use tools and frameworks that you know and love – Spring, Tomcat, WildFly, JBoss, WebLogic, WebSphere, Maven, Gradle, IntelliJ, Eclipse, Jenkins, Terraform and more.
Certification
Microsoft Certified: Azure Database Administrator Associate - Certifications
Administer an SQL Server database infrastructure for cloud, on-premises and hybrid relational databases using the Microsoft PaaS relational database offerings.
Documentation
Tutorial: Linux Java app with MongoDB - Azure App Service
Learn how to get a data-driven Linux Java app working in Azure App Service, with connection to a MongoDB running in Azure Cosmos DB.
Quickstart: Create a Java app on Azure App Service - Azure App Service
Deploy your first Java Hello World to Azure App Service in minutes. The Azure Web App Plugin for Maven makes it convenient to deploy Java apps.
Deploy and configure Tomcat, JBoss, or Java SE apps - Azure App Service
Learn how to deploy Tomcat, JBoss, or Java SE apps to run on Azure App Service and perform common tasks like setting Java versions and configuring logging.