{"id":15430524,"url":"https://github.com/lenisha/spring-jndi-appservice","last_synced_at":"2025-04-19T16:54:51.716Z","repository":{"id":42552806,"uuid":"142495190","full_name":"lenisha/spring-jndi-appservice","owner":"lenisha","description":"Example using Azure MSI library from Spring/JNDI application https://github.com/lenisha/msi-mssql-jdbc  article:","archived":false,"fork":false,"pushed_at":"2022-12-16T01:50:17.000Z","size":4082,"stargazers_count":3,"open_issues_count":7,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-18T06:16:07.015Z","etag":null,"topics":["application-insights","azure","azure-app-service","jndi","managed-service-identity","msi","spring","tomcat"],"latest_commit_sha":null,"homepage":"https://dzone.com/articles/migrating-java-applications-to-azure-app-service-p","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lenisha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-26T21:20:35.000Z","updated_at":"2022-04-17T12:16:28.000Z","dependencies_parsed_at":"2023-01-29T08:00:13.866Z","dependency_job_id":null,"html_url":"https://github.com/lenisha/spring-jndi-appservice","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lenisha%2Fspring-jndi-appservice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lenisha%2Fspring-jndi-appservice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lenisha%2Fspring-jndi-appservice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lenisha%2Fspring-jndi-appservice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lenisha","download_url":"https://codeload.github.com/lenisha/spring-jndi-appservice/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249743311,"owners_count":21319094,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-insights","azure","azure-app-service","jndi","managed-service-identity","msi","spring","tomcat"],"created_at":"2024-10-01T18:16:48.529Z","updated_at":"2025-04-19T16:54:51.701Z","avatar_url":"https://github.com/lenisha.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring/Hibernate App on Azure AppService (Tomcat) using JNDI to access SQL Server on Azure  \n\nCode for the article posted on dzone: https://dzone.com/articles/migrating-java-applications-to-azure-app-service-p\n\n## Spring Hibernate JNDI Example\n\nThis Spring example is based on tutorial [Hibernate, JPA \u0026 Spring MVC - Part 2] (https://www.alexecollins.com/tutorial-hibernate-jpa-spring-mvc-part-2/) by Alex Collins\n\nSpring application uses hibernate to connect to SQL Server. Hibernate is using container managed JNDI Data Source.\n\nThere are few options to configure JNDI for the web application running undet Tomcat (Tomcat JNDI)[https://www.journaldev.com/2513/tomcat-datasource-jndi-example-java] :\n\n- Application context.xml - located in app `META-INF/context.xml` - define Resource element in the context file and container will take care of loading and configuring it.\n- Server server.xml - Global, shared by applications, defined in `tomcat/conf/server.xml`\n- server.xml and context.xml - defining Datasource globally and including ResourceLink in application context.xml\n\nWhen deploying to Java application on Azure AppService, you can customize  out of the box managed Tomcat server.xml, but is not recommended as it will create snowflake deployement. That's why we will define JNDI Datasource on the **Application level**\n\n# Create Azure AppService and database\n\nIn Azure Portal create `Web App + SQL` and configure settings for the App to use Tomcat\n![Azure App Service config](https://github.com/lenisha/spring-jndi-appservice/raw/master/img/AppService.PNG  \"App Service Config\")\n\nCopy the Connection String from Azure SQL database\n![Azure SQL Connection](https://github.com/lenisha/spring-jndi-appservice/raw/master/img/ConnString.PNG \"Azure  SQL Server\")\n\nAdd the Connection String from Azure SQL database to **App Service / Application Settings**   settings\n\n** DO NOT INCLUDE USERNAME/PASSWORD **\n\n![Azure SQL Connection](https://github.com/lenisha/spring-jndi-appservice/blob/msijdbc/img/ConnectionString.PNG \"Azure App Service Settings\")\n\nDB connection url for Azure SQL is usually in thins format `jdbc:sqlserver://server.database.windows.net:1433;database=db;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;`\n\nAdding Setting `JAVA_OPTS` with value `-D\u003cconnection name\u003e=\u003cjdbc url\u003e`  would create environment variable `connection name` available to the Java application.\n In our example above - `SQLDB_URL`\n\n\n# Managed Service Identity\nAdd MSI to AppService and grant it permissions to SQL database\n\n```\naz webapp identity assign --resource-group \u003cresource_group\u003e --name \u003capp_service_name\u003e\naz ad sp show --id \u003cid_created_above\u003e\n\naz sql server ad-admin create --resource-group \u003cresource_group\u003e --server-name \u003csql_server_name\u003e  --display-name \u003cadmin_account_name\u003e --object-id \u003cid_created_above\u003e\n```\n\nExample:\n\n```\naz webapp identity assign --resource-group jnditest --name testjndi\naz ad sp show --id 82cc5f96-226a-4721-xxxxx-xxxxxxx\n\naz sql server ad-admin create --resource-group jnditest --server-name jnditestsrv  --display-name admin-msi --object-id 82cc5f96-226a-4721-902c-xxxxxx\n```\n\n# Define DataSource\n\n\nTo define JNDI Datasource for Tomact Application, add file `META-INF/context.xml` to the application.\nIn this example it's added to `main/webapp/META-INF/context.xml` anc contains the following datasource definition\nThis solution uses default db pool in Tomcat (typically Apache Commons DBCP) and native JDBC driver capability to get and refresh MSI tokens.\n\n```\n\u003cContext\u003e\n    \u003cResource auth=\"Container\" \n\t    driverClassName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"\n\t    maxActive=\"8\" maxIdle=\"4\" validationQuery=\"SELECT 1\" testOnBorrow=\"true\"\n\t    name=\"jdbc/tutorialDS\" type=\"javax.sql.DataSource\"\n\t    url=\"${SQLDB_URL}\" /\u003e\n\u003c/Context\u003e\n```\nwhere\n- `url` points to url, in the example above provided by environment variable set by `JAVA_OPTS=-DSQLDB_URL=jdbc:sqlserver://...'\n\n## Enable MSI \n\nInclude flag `authentication=ActiveDirectoryMSI` in  jdbcURL to enable MSI based authentication for the connection:\n\n```\njdbc:sqlserver://\u003csrv\u003e.database.windows.net:1433;database=\u003cdb\u003e;authentication=ActiveDirectoryMSI;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;\u003c/value\u003e\n``` \n\n## Use SQL Server Hibernate Dialect\n\nin `resources\\META-INF\\persistence.xml`\n\n```\n\u003cproperty name=\"hibernate.dialect\" value=\"org.hibernate.dialect.SQLServerDialect\" /\u003e\n```\n\n## Update Application dependencies\nJDBC driver for SQL server `sqljdbc42.jar` installed in Tomcat in Azure App Service by default is older version that does not support token authentication,\nInclude newer version that supports token based Authentication along with the app.\n\n```\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.microsoft.sqlserver\u003c/groupId\u003e\n        \u003cartifactId\u003emssql-jdbc\u003c/artifactId\u003e\n        \u003cversion\u003e7.2.0.jre8\u003c/version\u003e\n     \u003c/dependency\u003e\n\n```\n\n## To test locally:\n`mvn initialize`\n`mvn clean  package`\nand copy resulting war file from target directory to Tomcat webapps directory\n\nNavigate to `localhost:8080/spring-jndi-appservice/create-user.html`\n\n## To debug locally\nset env variables\n```\nset JAVA_OPTS=-DSQLDB_URL=jdbc:sqlserver://server.database.windows.net:1433;database=database;authentication=ActiveDirectoryMSI;encrypt=true;trustServerCerti\nficate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;\n\nset JPDA_SUSPEND=y\n\ncatalina.bat jpda start\n```\n\n\n## Deploy on Azure AppService\n\nTo test on Azure AppService deploy using Azure deployment options, the simplest is to use FTP:\n\n### FTP\n  - Get the FTP hostname and credentials from the App Service blade\n  - Upload war file to `d:\\home\\site\\wwwroot\\webapps`  \n  - Upload web.config file to `d:\\home\\site\\wwwroot`\n  - Restart App Service\n  - Navigate to `https://appname.azurewebsites.net/tutorial-hibernate-jpa/create-user.html`\n\n### Maven plugin\n- setup authentication in .m2/settings.xml as described in https://docs.microsoft.com/en-us/java/azure/spring-framework/deploy-spring-boot-java-app-with-maven-plugin\n\n- Add plugin definition in pom.xml\n```\n      \u003cplugin\u003e\n                 \u003cgroupId\u003ecom.microsoft.azure\u003c/groupId\u003e\n                 \u003cartifactId\u003eazure-webapp-maven-plugin\u003c/artifactId\u003e\n                 \u003cversion\u003e1.3.0\u003c/version\u003e\n                 \u003cconfiguration\u003e\n                     \u003cauthentication\u003e\n                         \u003cserverId\u003eazure-auth\u003c/serverId\u003e\n                     \u003c/authentication\u003e\n                      \u003c!-- Web App information testjndiapp2 --\u003e\n                    \u003cresourceGroup\u003ejnditest\u003c/resourceGroup\u003e\n                    \u003cappName\u003etestjndiai\u003c/appName\u003e\n                    \u003c!-- \u003cregion\u003e and \u003cpricingTier\u003e are optional. They will be used to create new Web App if the specified Web App doesn't exist --\u003e\n                    \u003cregion\u003eeastus\u003c/region\u003e\n                    \u003cpricingTier\u003eS1\u003c/pricingTier\u003e\n     \n                    \u003c!-- Java Runtime Stack for Web App on Windows--\u003e\n                    \u003cjavaVersion\u003e1.8.0_181\u003c/javaVersion\u003e\n                    \u003cjavaWebContainer\u003etomcat 7.0.81\u003c/javaWebContainer\u003e\n                    \u003cstopAppDuringDeployment\u003etrue\u003c/stopAppDuringDeployment\u003e\n     \n                    \u003cappSettings\u003e\n                      \u003cproperty\u003e\n                           \u003cname\u003eJAVA_OPTS\u003c/name\u003e\n                           \u003cvalue\u003e-DSQLDB_URL=jdbc:sqlserver://\u003csrv\u003e.database.windows.net:1433;database=\u003cdb\u003e;authentication=ActiveDirectoryMSI;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;\u003c/value\u003e\n                       \u003c/property\u003e\n     \n                         \u003c!-- Add APPLICATION_INSIGHTS_IKEY - Instrumentation key--\u003e\n                    \u003c/appSettings\u003e\n     \n                     \u003cdeploymentType\u003eftp\u003c/deploymentType\u003e\n                     \u003c!-- Resources to be deployed to your Web App --\u003e\n                     \u003cresources\u003e\n                         \u003cresource\u003e\n                             \u003c!-- Where your artifacts are stored --\u003e\n                             \u003cdirectory\u003e${project.basedir}/target\u003c/directory\u003e\n                             \u003c!-- Relative path to /site/wwwroot/ --\u003e\n                             \u003ctargetPath\u003ewebapps\u003c/targetPath\u003e\n                             \u003cincludes\u003e\n                                 \u003cinclude\u003e*.war\u003c/include\u003e\n                             \u003c/includes\u003e\n                         \u003c/resource\u003e\n                     \u003c/resources\u003e\n                 \u003c/configuration\u003e\n              \u003c/plugin\u003e\n\t\n```\n- run `mvn azure-webapp:deploy`\n\nExample output:\n```\n[INFO] --- azure-webapp-maven-plugin:0.2.0:deploy (default-cli) @ tutorial-hibernate-jpa ---\nAI: INFO 25-03-2018 19:31, 1: Configuration file has been successfully found as resource\nAI: INFO 25-03-2018 19:31, 1: Configuration file has been successfully found as resource\n[INFO] Start deploying to Web App testjavajndi...\n[INFO] Authenticate with ServerId: azure-auth\n[INFO] [Correlation ID: 462a8a7f-c2ec-40c8-a43b-80be705225b8] Instance discovery was successful\n[INFO] Updating target Web App...\n[INFO] Successfully updated Web App.\n[INFO] Using 'UTF-8' encoding to copy filtered resources.\n[INFO] Copying 1 resource to C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi\\webapps\n[INFO] Starting uploading files to FTP server: waws-prod-yt1-005.ftp.azurewebsites.windows.net\n[INFO] Starting uploading directory: C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi --\u003e /site/wwwroot\n[INFO] [DIR] C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi --\u003e /site/wwwroot\n[INFO] ..[DIR] C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi\\webapps --\u003e /site/wwwroot/webapps\n[INFO] ....[FILE] C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi\\webapps\\tutorial-hibernate-jpa.war --\u003e /site/wwwroot/webapps\n[INFO] ...........Reply Message : 226 Transfer complete.\n\n[INFO] Finished uploading directory: C:\\projects\\tutorial-hibernate-jpa\\target\\azure-webapps\\testjavajndi --\u003e /site/wwwroot\n[INFO] Successfully uploaded files to FTP server: waws-prod-yt1-005.ftp.azurewebsites.windows.net\n[INFO] Successfully deployed Web App at https://testjavajndi.azurewebsites.net\n``` \n\n## Change LOG LEVEL at Runtime\nTo be able to change log level at runtime use Log4J version 2 ( docs on the variables https://logging.apache.org/log4j/2.0/manual/lookups.htm )\n\n- In `log4j.properties` set the log level to use variable, e.g\n\n```\nlog4j.logger.tutorial=${APP_LOG_LEVEL}, file, aiAppender\nlog4j.logger.com.microsoft=${MS_LOG_LEVEL}, file, aiAppender\n```\n\n- Set variable in App service environment settings in `JAVA_OPTS`\n```\n \u003cappSettings\u003e\n \u003cproperty\u003e\n      \u003cname\u003eJAVA_OPTS\u003c/name\u003e\n      \u003cvalue\u003e-DAPP_LOG_LEVEL=INFO -DMS_LOG_LEVEL=ERROR\u003c/value\u003e\n  \u003c/property\u003e\n \u003c/appSettings\u003e\n```\n- Update Application setting and the new Log Level will take effect in a few seconds\n\n## Azure App Insights\n\nEnable Azure App Insights for monitoring and application log aggregation. The process describe on Azure docs web site:\n[App Insights for Java](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-java-get-started)\n\nFor instructions on AppInsights  - see branch `appinsights`\n\n## Additional notes on environment indirection\n\nJDBC driver for SQL server `sqljdbc4jar` is installed in Tomcat in Azure App Service by default is old version, need to include most recent version supporting AzureAD in `pom.xml`\nTo define JNDI Datsource for Tomact Application, add file `META-INF/context.xml` to the application.\nIn this example it's added to `main/webapp/META-INF/context.xml` anc contains the following datasource definition\n\n```\n\u003cContext\u003e\n    \u003cResource auth=\"Container\"\n\t    driverClassName=\"com.microsoft.sqlserver.jdbc.SQLServerDriver\"\n\t    maxActive=\"8\" maxIdle=\"4\"\n\t    name=\"jdbc/tutorialDS\" type=\"javax.sql.DataSource\"\n\t    url=\"${SQLAZURE_UsersDB}\" /\u003e\n\n\u003c/Context\u003e\n```\n\nNotice that the URL for the database uses environment variable that should be available and processed by Tomcat startup.\nUnfortunately directly reading App Settings Connection string environment varibale does not work, due to the way Tomcat is started by Azure App Service lifecycle.\nSo we will use **indirection** or what was called in C++ world a pointer to a variable.\n\nThe way to enforce Tomcat to read environment variables and make them available to application is to define them in `JAVA_OPTS` paramaters during Tomcat startup.\n\n#### Define SQL Connection env varible for JAVA_OPTS\nAs discussed in [How to set env in Java app in Azure App Service](https://blogs.msdn.microsoft.com/azureossds/2015/10/09/setting-environment-variable-and-accessing-it-in-java-program-on-azure-webapp/)\nTo set environment variable that uses another variable definition (not direct value) is to override it in `web.config`\n\nAnd here are our configuration where we are passing in `JAVA_OPTS` to tomcat  `SQLAZURE_UsersDB`variable in that is used in our DataSource definition above. The value of it relies on the fact that we have set in Application Settings Database Connection string called `UsersDB`, as discussed previously.\n\n```\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cconfiguration\u003e\n  \u003csystem.webServer\u003e\n    \u003chandlers\u003e\n      \u003cremove name=\"httpPlatformHandlerMain\" /\u003e\n      \u003cadd name=\"httpPlatformHandlerMain\" path=\"*\" verb=\"*\" modules=\"httpPlatformHandler\" resourceType=\"Unspecified\"/\u003e\n    \u003c/handlers\u003e\n    \u003chttpPlatform processPath=\"%AZURE_TOMCAT7_HOME%\\bin\\startup.bat\"\u003e\n        \u003cenvironmentVariables\u003e\n            \u003cenvironmentVariable name=\"JAVA_OPTS\" value=\"-DSQLAZURE_UsersDB=%SQLAZURECONNSTR_UsersDB%\"/\u003e\n        \u003c/environmentVariables\u003e\n      \u003c/httpPlatform\u003e\n  \u003c/system.webServer\u003e\n\u003c/configuration\u003e\n```\n\nThis file `web.config` should be copied to `D:\\home\\site\\wwwroot` on the AppService to override Tomcat loading.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flenisha%2Fspring-jndi-appservice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flenisha%2Fspring-jndi-appservice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flenisha%2Fspring-jndi-appservice/lists"}