Windows Azure and Java: Working with Service Runtime Configuration

When working with applications in Windows Azure, code is deployed to one or more Worker Role or Web Role instances. Worker and Web Roles are Windows Server 2008, with the primary difference of a Web Role having IIS enabled. A role also contains some scaffolding code representing the role entry point. This entry point is used for start-up and shutdown, as well as processing when a role instance is in a ready-state.

With these roles, runtime settings should ideally be configurable without rebuilding or redeploying the application. In a typical Web application, the following runtime configuration settings (among others) are typically stored in the configuration files:

  • Database connection strings
  • Networking endpoints
  • File system paths
  • Timeout settings
  • Number of application instances

Storing these runtime configuration settings in a configuration file allows for deploying an application in different environments such as development, testing, staging, and production. In a production environment, an application typically communicates with different Web services, databases, and storage accounts than when running in a development or staging environment. Storing runtime settings in configuration files (rather than embedding them in code) allows for changes to these settings without touching the source code. This also means that an application doesn’t have to be recompiled, which simplifies the deployment process.

 

In Windows Azure, application configuration files are bundled with the application to create a single deployment package. If a change to a configuration file is needed, a new deployment package must be built and uploaded to Windows Azure. This is not an ideal scenario because it requires code redeployment with a configuration change.

 Windows Azure Configuration Files

Fortunately, Windows Azure offers a way to store runtime configuration settings outside of the deployment package. Each Windows Azure project uses a pair of XML configuration files to define the runtime configuration:

1.     ServiceDefinition.csdef

The Windows Azure service definition file specifies the service model for an application, including:

  • Role definitions
  • Service endpoints (network ports and protocols)
  • Configuration setting names (but no values) for each role

          The complete schema of the service definition file is here.

This file is included in the Windows Azure deployment package and may not be changed without redeploying the application.

2.     ServiceConfiguration.cscfg

The Windows Azure service configuration file specifies:

  • Number of instances to deploy for each role
  • Configuration setting values
  • Thumbprints for each role’s certificates

              The schema of this file can be accessed here.

 

This file is separate from the Windows Azure deployment package, allowing it to be changed independently of the deployed application. Additionally, changes to the configuration file do not require changes to the application.

Service Configuration

The configuration settings in the Service Configuration file are typically application-level properties, which means properties specific to the Java application. For example, database connection string. Configuration settings may be changed at any time, without needing to re-generate and re-deploy the Windows Azure package file. In other words, there’s no need to rebuild the Azure project. While the Windows Azure portal provides the ability to edit the configuration file manually, this is typically an automated task where a new configuration file is uploaded, causing the running application to then take appropriate actions to deal with new application-level settings.

Updating the Service Configuration File Manually

To update the configuration file manually, perform the following steps:

1.     Login to the Windows Azure portal.

2.     In the left panel, click Hosted Services, Storage Accounts & CDN.

3.     Select your deployed application and click Configure.

A Configure Deployment wizard appears. In this wizard, you can either upload a new/updated Service Configuration file or edit the current configuration file. As soon as changes are made to this file, the deployed application receives a notice informing that the configuration file has changed. By default, this causes a reboot of all running role instances. You may choose to avoid the reboot by intercepting this notification and taking an action accordingly. While the latter provides a faster response to configuration changes because a reboot is avoided, you must be cautious because some settings may have a big impact on an application’s runtime environment. Making changes to a live environment may have adverse effects. The safest (albeit slower) approach is to let the role instances reboot and read all new settings as the application restarts.

Accessing Service Configuration in a Java Application

The Windows Azure Service Runtime library provides a REST API, allowing applications to interact with the Windows Azure environment and determine information about the roles, role instances, and the role environment. The Windows Azure SDK for Java wraps the REST API. This SDK, provided as a JAR file, is available through the Windows Azure site’s Java Developer Center. Alternatively, all source code is available from the github  repository.

 

In the following example, we extend our HelloWorld Web application (available here for download) to access the configuration with the help of the Java SDK Service Runtime library. We will retrieve configuration settings such as DBconnection properties for SQL Azure from the ServiceConfiguration.cscfg file.

 

1.     Create WindowsAzureProject using Windows Azure plugin for Eclipse (also available here for download).

2.     Select the ServiceDefinition.csdef file to edit.

3.     Add the following settings for jdbc properties under the <workerRole> element (after the </Endpoints> tag). Note that there are no values; just names are stored here.

 

  <ConfigurationSettings>

     <Setting name=“jdbc.driverClassName”/>

<Setting name=“jdbc.url”/>

<Setting name=“jdbc.username”/>

<Setting name=“jdbc.password”/>

  </ConfigurationSettings>

 

4.     Select the ServiceConfiguration.cscfg file to edit, and then add the settings displayed below under the <ConfigurationSettings> tag.

Notice that the names of all settings match the configuration setting names provided in the Service Definition file, but now they have values as well.

   

 <Setting name=“jdbc.driverClassName” 

         value=“com.microsoft.sqlserver.jdbc.SQLServerDriver”/>

 <Setting name=“jdbc.url”           

         value=“jdbc:sqlserver://azure.database.windows.net;

                databaseName=CloudNinjaPrimary”/>

  <Setting name=“jdbc.username” value=“admin@azure”/>

  <Setting name=“jdbc.password” value=“password”/>

 

5.     Navigate to the HelloWorld Web application.

6.     Add the latest Windows Azure SDK for Java under HelloWorld/WebContent/WEB-INF/lib. Currently, the latest version is microsoft-windowsazure-api-0.2.0.jar (as of this writing). The JAR file can be downloaded from the Java Developer Center.

7.       Edit the index.jsp file in Eclipse.

a.     Import the RoleEnvironment class along with other classes listed below.

<%@ page import=“com.microsoft.windowsazure.serviceruntime.RoleEnvironment”%>

<%@ page import=“java.util.Map”%>

<%@ page import=“java.util.HashMap”%>

The RoleEnvironment class provides information for all running role instances within a deployment, including configuration settings, endpoints, and instance status. We will use this class to retrieve configuration values. This class also provides several operations that may be performed within the role environment. We’ll go over these shortly.

 b.      Replace the <body> tag content with the code listed below.   

<%

   Map<String, String> configSettingsMap =

       new HashMap<String, String>();

   String jdbcDriver = null;

   String jdbcUrl = null;

   String jdbcUserName = null;

   String jdbcPassword = null;

   //Check whether the role instance is running in the Windows Azure      

    environment.

   if (RoleEnvironment.isAvailable()) {

      configSettingsMap =       RoleEnvironment.getConfigurationSettings();

      jdbcDriver = configSettingsMap.get(“jdbc.driverClassName”);

      jdbcUrl = configSettingsMap.get(“jdbc.url”);

      jdbcUserName = configSettingsMap.get(“jdbc.username”);

      jdbcPassword = configSettingsMap.get(“jdbc.password”);

 }

%>

-The RoleEnvironment.isAvailable method returns the value true if the application is running in the Windows Azure  environment, whether in the local compute emulator or in the “real” cloud. If it returns false, this means the code is executing outside of the cloud. If RoleEnvironment.isAvailable is true, a call to RoleEnvironment.isEmulated will indicate whether the application is running in the local emulator or in the cloud.

 -The RoleEnvironment.getConfigurationSettings method retrieves the names and values of all settings in the service configuration file and returns a Map object with these settings.
  c.     Add the code listed below to display the retrieved settings and the values in the tabular format. 

<%  

   if (!configSettingsMap.isEmpty()) { %> 

      <table border=’1′>

         <tr>

            <th>Property</th>

            <th>Value</th>

         </tr>

         <tr>

            <td>jdbc.driverClassName</td>

            <td><%=jdbcDriver%></td>

         </tr>

         <tr>

            <td>jdbc.url</td>

            <td><%=jdbcUrl%></td>

         </tr>

         <tr>

            <td>jdbc.username</td>

            <td><%=jdbcUserName%></td>

         </tr>

         <tr>

             <td>jdbc.password</td>

             <td><%=jdbcPassword %></td>

         </tr>

      </table>

<%    } %>

 

8.     Create a WAR file of the HelloWorld Java project, and then export it to <your_project_location>\WindowsAzureProject\WorkerRole1\approot>

9.     Build the project and deploy it in the compute emulator. This is done by running RunInEmulator.cmd, located in the Windows Azure project, under the emulatorTools folder.

10.   In the browser, navigate to http://<Context>/HelloWorld.

The output of your HelloWorld application (the output of index.jsp file) appears similar to the following screenshot. 

 The complete code sample for accessing service runtime configuration is available from our github repository.

The Java application can access the role environment via the Service Runtime library only if the application is deployed in the Azure environment or in compute emulator. A developer might want to develop an application that is designed for both Windows Azure and non-Windows Azure environments. For this, the RoleEnvironment.isAvailable method can be used. This method returns true if the application is running in Azure environment or compute emulator. Otherwise, it returns false. Using this method, a Java developer can devise a fallback mechanism to read configuration settings from a .properties file if the application is running under a non-Windows Azure environment.

Operations using the RoleEnvironment Class

Following are some of the operations that you can perform using the RoleEnvironment class:

  • Get deployment roles
  • Get access to local storage resources
  • Change the status of a role instance
  • Add callback methods for changes to the role environment

Getting Deployment Roles

The roles available in a deployment are specified in the ServiceDefinition.csdef file. We can get the complete set of the deployed roles using the method RoleEnvironment.getRoles of the RoleEnvironment class. This method returns the set of all deployed roles. The retrieved roles are represented by an object of the Role class.

To get the current role instance, we can use the RoleEnvironment.getCurrentRoleInstance method, which returns the RoleInstance class object. This object is then used to retrieve the current role using the RoleInstance.getRole method.

Getting Access to Local Storage Resources

An application deployed to Windows Azure is placed in a specific file directory on each virtual machine, for a given Role. This file directory has limited space. Each VM also provides optional scratch storage, ranging from 20GB to 2TB depending on VM size, for an application to use.  Local storage is useful for cache data, which the application needs to access frequently. It’s not intended for permanent storage, as it is considered non-durable with no redundancy or geo-replication. If an application needs durable storage, it may store content in Windows Azure Blobs and Tables, as well as SQL Azure. For more details on VM size and available Local Storage, see this article.

Local storage resources are declared in the service definition file. Each local storage resource is reserved for every instance of the role. The minimum amount of space that can be allocated to a resource is 1 MB, and the maximum storage allocation across all Local Resources is dependent on the VM size. Local resources may optionally be cleaned up during a VM reboot.

Local storage resources are defined under LocalResources element in the service definition file. LocalResources is a child element of the WorkerRole element and contains namesizeInMB, and cleanOnRoleRecycle as attributes.

Example of a local storage resource:     

 <LocalResources>

   <LocalStorage name=”localStore1″ sizeInMB=”5″ />

   <LocalStorage name=”localStore2″ sizeInMB=”10″ cleanOnRoleRecycle=”false” />

 </LocalResources>

To get local storage resources, we use the RoleEnvironment.getLocalResourcemethodthat returns the collection of LocalResouces objects. A LocalResouce object provides the resource information such as its name and size. It also gives the absolute path of the local resource root directory, using the LocalResouce.getRootPath method. We can perform read and write operations on this directory using Java I/O classes.

Changing the Status of a Role Instance

The status of a role instance represents the combined status of various processes running inside an instance. A role instance status can be Busy or Ready. When the status of a role instance is Busy, the instance does not receive any request from the load balancer. When the status is Ready, the instance is available to receive requests from the load balancer.

The role instance status can be changed by using the RoleEnvironment.setStatus method. While changing the status, the status expiry period is also specified in the method. Before a status expires, it is important to set or clear the status again.  The RoleEnvironment.clearStatus method is used to clear the status, and it discards any previous calls to setStatus. Note that the failure to set or clear the status before the expiry of the previous setStatus call results in Windows Azure considering your role instance unresponsive.

Let’s consider a scenario where the status of a role instance needs to be changed explicitly. Consider an application that is deployed on Windows Azure and is dependent on a third-party Web service. If the third-party Web service is temporarily unavailable, having the application instance status as Ready may not make any sense. In such a case, a developer can opt to set the status of the role instance as Busy until the Web service is available. Once the service is available, the status can again be set to Ready.

Adding Callback Methods for Changes to the Role Environment

The Service Runtime library provides three listener interfaces that listen to the changes in a role environment. These listeners are registered using the RoleEnvironment class:

  • RoleEnvironmentChangingListener
  • RoleEnvironmentChangedListener
  • RoleEnvironmentStoppingListener

RoleEnvironmentChangingListener

This is a listener for the Changing event that occurs after a new configuration file has been submitted to Windows Azure and before the change is applied to the running instance of the role. Service configuration changes are applied on the fly to running role instances. This event can also be cancelled to prevent the configuration change.

If a changing listener is added to a role, the configuration changes are applied immediately without rebooting the instances. However, sometimes it’s advisable to let the role instances reboot and read all new settings as the application restarts. Applying new configuration settings on the fly in the production environment could be tricky, especially if the runtime environment has been customized. It might have side effects or adverse situations if the application was simply restarted.

RoleEnvironmentChangedListener

This is a listener for the Changed event that occurs after a configuration change has been applied to a role instance.

 

RoleEnvironmentStoppingListener

This is a listener for the Stopping event that occurs when the role is stopping.

Summary

In this article, we discussed how to avoid hard-coding application-specific settings by specifying them in the Service Configuration file. The advantage of using the configuration file is that you can change the settings without modifying and repackaging the application code. Moreover, the changes can be applied immediately or after the reboot of the role instance. The listener interfaces provided by the Service Runtime library let you decide whether to reboot a role instance while applying the configuration changes to the role environment. You can use a listener interface implementation to avoid reboot in case of applications for which rebooting might be a lengthy affair. We also discussed the RoleEnvironment class that is used to interact with the role environment. This class can register various role environment listeners and can provide methods to get role information, access local storage resources, and so on.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>