Gaurav Mantri's Personal Blog.

Consuming Windows Azure Service Management API In Java

I have written some blog posts about consuming Windows Azure Service Management API using C#. I have been quite active on StackOverflow trying to answer some questions about Windows Azure. Recently I observed a number of questions there asking for consuming this API through Java. Also somebody approached me asking for something like this. This blog post is a result of that. In this post, I will talk about how one can consume this API using Java.

Its been ages that I have worked with Java so things I describe below may sound trivial to experienced Java developers so please bear with me Smile. Also if there’re better ways to do things, please let me know as most of the code you will see below would be the result of massive Internet searches Smile.

About Windows Azure Service Management API

Let’s take a moment to talk briefly about what this API is and some other fundamental things about it and then we’ll get into the code.

What?

Windows Azure Service Management API is a REST based API for managing Windows Azure resources. Using this API, you can manage your cloud services, storage accounts, virtual machines, virtual networks and lot more. Essentially the things you could do on Windows Azure Portal can be done through this API. In fact, Windows Azure Portal is built on top of this API. You can learn more about this here: http://msdn.microsoft.com/en-us/library/windowsazure/ee460799.aspx.

How it Works?

To use this API, you would need 2 things:

  1. Subscription Id: This is a GUID which uniquely identifies your subscription. You can get this information through portal.
  2. Management Certificate: A management certificate is required to authenticate your API calls. This management certificate must be associated with your subscription which you can do by upload the certificate in the portal. You could either create a management certificate on your own and associate it with your subscription (this is covered in details in this post) or you could ask Windows Azure platform to create a management certificate for you and associate it with your subscription. This is done through a process called “Download Publish Profile File”. I have written a blog post about it some days back which you can read here: https://gauravmantri.com/2012/09/14/about-windows-azure-publish-settings-file-and-how-to-create-your-own-publish-settings-file/.

Since the API is a REST based API, the endpoints are accessible over HTTP. In your code, you create an endpoint specific to a particular kind of operation you want to perform. Then you create an HTTP request for that endpoint. To authenticate the request, you attach the management certificate along with that request. You can learn more about the authentication here: http://msdn.microsoft.com/en-us/library/windowsazure/ee460782.aspx.

Now that we know a bit about the API, let’s look at how we can consume this API using Java.

Using Publish Profile File

I will not go into details about consuming this API using the publish profile file as it is covered extremely well by Andy Cross and Richard Conway in their Elastacloud blog post: http://blog.elastacloud.com/2012/09/29/a-windows-azure-service-management-client-in-java/. If this is something you’re looking for, stop reading this post and head over to their website otherwise read on Smile.

Using Management Certificate

Rest of this post will cover how you could consume this API by creating your own management certificate. There are some things you would need to do first.

Step 1: Create a Keystore

Coming from .Net world, I was used to find certificate in certificate store and obviously I tried to do the same in Java as well Smile. Turns out, I have to create something called a Keystore. To create a Keystore, you would use a tool called (guess what Smile) Keytool. Here’s the command I used to create a Keystore:

keytool -genkeypair -alias mydomain -keyalg RSA -keystore WindowsAzureKeyStore.jks 
-keysize 2048 -storepass "test123";

image

What we’ve done is created a Keystore called “WindowsAzureKeyStore.jks” and set the password to access this as “test123”. You should see a file called “WindowsAzureKeyStore.jks” in “C:\Program Files\Java\jre7\bin” folder.

image

Step 2: Export Management Certificate

Next we’ll export a certificate from this Keystore we just created. To do so, again I will use Keytool. Here’s the command I used:

keytool -v -export -file D:\WindowsAzureSMAPI.cer -keystore WindowsAzureKeyStore.jks 
-alias mydomain

image

Once this operation completes, I will get a “WindowsAzureSMAPI.cer” file in “D:\” of my computer.

image

Step 3: Upload Certificate

Next step is to upload this certificate to the Windows Azure Portal. To do so, login into Windows Azure Portal at https://manage.windowsazure.com and click on “SETTINGS” tab and then go to “MANAGEMENT CERTIFICATES” tab and upload this “WindowsAzureSMAPI.cer” file there.

image

The Code

Now we’re ready to code Smile.

Step 1: Get Keystore

Since we need the management certificate to authenticate the request and the certificate is in the Keystore, first we will get it. To open a Keystore, we would need the full path of the Keystore we just created and it’s password. Here’s the code to do so:

	private static KeyStore getKeyStore(String keyStoreName, String password) throws IOException
	{
		KeyStore ks = null;
		FileInputStream fis = null;
		try {
			ks = KeyStore.getInstance("JKS");
			char[] passwordArray = password.toCharArray();
			fis = new java.io.FileInputStream(keyStoreName);
			ks.load(fis, passwordArray);
			fis.close();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally {
			if (fis != null) {
				fis.close();
			}
		}
		return ks;
	}

Step 2: Get SSLSocketFactory

Here’s the code to get SSLSocketFactory:

	
	private static SSLSocketFactory getSSLSocketFactory(String keyStoreName, String password) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, IOException {
		KeyStore ks = getKeyStore(keyStoreName, password);
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
		keyManagerFactory.init(ks, password.toCharArray());

		  SSLContext context = SSLContext.getInstance("TLS");
		  context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());

		  return context.getSocketFactory();
	}

Step 3: Execute HTTP Requests

Once we have this, all we have to do is execute HTTP requests.

Step 3.1: Create URL

Just create a URL based on the operation you want to perform. For example, assuming your subscription id is “fd9a283a-3a98-b17e-a2b8-763cf6e9ba18” and you want to perform “List Locations” operation, your URL would be: https://management.core.windows.net/fd9a283a-3a98-b17e-a2b8-763cf6e9ba18/locations

Step 3.2: Create HttpsURLConnection object

Using this URL, create an instance of HttpsUrlConnection object and set SSLSocketFactory.

		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);

Step 3.3: Provide other necessary information

Specify other necessary information like required request method, request headers, content type etc. and execute the request. For example, if you want to perform “List Locations” operation, you would need to perform a “Get” request. The code would look something like this:

		con.setRequestMethod("GET");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		InputStream responseStream = (InputStream) con.getContent();
		String response = getStringFromInputStream(responseStream);
		responseStream.close();
		return response;

That’s pretty much to it!

Helper Functions

As I was working on this, I abstracted some functionality and created some helper functions for them. Here they are for your convenience:

Perform “Get” Operation

	private static String processGetRequest(URL url, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setRequestMethod("GET");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		InputStream responseStream = (InputStream) con.getContent();
		String response = getStringFromInputStream(responseStream);
		responseStream.close();
		return response;
	}

Perform “Post” Operation

	private static int processPostRequest(URL url, byte[] data, String contentType, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = null;
		con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setDoOutput(true);
		con.setRequestMethod("POST");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		con.setRequestProperty("Content-Length", String.valueOf(data.length));
		con.setRequestProperty("Content-Type", contentType);
		
		DataOutputStream  requestStream = new DataOutputStream (con.getOutputStream());
		requestStream.write(data);
		requestStream.flush();
		requestStream.close();
		return con.getResponseCode();
	}

Perform “Delete” Operation

	private static int processDeleteRequest(URL url, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = null;
		con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setRequestMethod("DELETE");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		return con.getResponseCode();

Working Examples

Here are some working examples. They are not complete but should give you an idea about how to go about performing some operations:

List Locations

This is based on the documentation here: http://msdn.microsoft.com/en-us/library/windowsazure/gg441293.aspx. This makes use of “processGetRequest” helper function.

			String subscriptionId = "fd9a283a-3a98-b17e-a2b8-763cf6e9ba18";
			String keyStorePath = "C:\\Program Files\\Java\\jre7\\bin\\WindowsAzureKeyStore.jks";
			String keyStorePassword = "test123";
			String url = "";

			//List locations
			url = String.format("https://management.core.windows.net/%s/locations", subscriptionId);
			String response = processGetRequest(new URL(url), keyStorePath, keyStorePassword);
			System.out.println(response);

and here’s the output:

<Locations xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Location>
    <Name>Southeast Asia</Name>
    <DisplayName>Southeast Asia</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>East Asia</Name>
    <DisplayName>East Asia</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>North Central US</Name>
    <DisplayName>North Central US</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>North Europe</Name>
    <DisplayName>North Europe</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>West Europe</Name>
    <DisplayName>West Europe</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>West US</Name>
    <DisplayName>West US</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
  <Location>
    <Name>East US</Name>
    <DisplayName>East US</DisplayName>
    <AvailableServices>
      <AvailableService>Compute</AvailableService>
      <AvailableService>Storage</AvailableService>
      <AvailableService>PersistentVMRole</AvailableService>
      <AvailableService>HighMemory</AvailableService>
    </AvailableServices>
  </Location>
</Locations>

You can then simply parse the XML to get desired information.

Create Cloud Service

This is based on the documentation here: http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx. This makes use of “processPostRequest” helper function.

			String subscriptionId = "fd9a283a-3a98-b17e-a2b8-763cf6e9ba18";
			String keyStorePath = "C:\\Program Files\\Java\\jre7\\bin\\WindowsAzureKeyStore.jks";
			String keyStorePassword = "test123";
			String url = "";
			String cloudServiceName = "cloudserviceusingjava";
			String label = "my awesome cloud service in java";
			String description = "my awesome cloud service in java";
			String location = "Southeast Asia";
			url = String.format("https://management.core.windows.net/%s/services/hostedservices", subscriptionId);
			String requestBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?><CreateHostedService xmlns=\"http://schemas.microsoft.com/windowsazure\"><ServiceName>%s</ServiceName><Label>%s</Label><Description>%s</Description><Location>%s</Location></CreateHostedService>";
			requestBody = String.format(requestBody, cloudServiceName, Base64.encodeBase64String(label.getBytes()), description, location);
			int createResponseCode = processPostRequest(new URL(url), requestBody.getBytes(), "application/xml", keyStorePath, keyStorePassword);
			System.out.println(createResponseCode);

If the request process properly, you should get back 201 as “responseCode”.

Since the code above required me to convert a string into Base64 format, I made use of “apache.common.codecs” which you can download from here: http://commons.apache.org/proper/commons-codec/download_codec.cgi.

Delete Cloud Service

This is based on the documentation here: http://msdn.microsoft.com/en-us/library/windowsazure/gg441305.aspx/. This makes use of “processDeleteRequest” helper function.

			String subscriptionId = "fd9a283a-3a98-b17e-a2b8-763cf6e9ba18";
			String keyStorePath = "C:\\Program Files\\Java\\jre7\\bin\\WindowsAzureKeyStore.jks";
			String keyStorePassword = "test123";
			String url = "";
			String cloudServiceToDelete = "cloudserviceusingjava";
			url = String.format("https://management.core.windows.net/%s/services/hostedservices/%s", subscriptionId, cloudServiceToDelete);
			int deleteResponseCode = processDeleteRequest(new URL(url), keyStorePath, keyStorePassword);
			System.out.println(deleteResponseCode);

If the request process properly, you should get back 200 as “responseCode”.

Complete Code

Here’s the complete code:

import java.io.IOException;
import java.net.*;
import java.security.*;
import java.io.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.codec.binary.Base64;

public class ServiceManagementRESTHelper {

	private static KeyStore getKeyStore(String keyStoreName, String password) throws IOException
	{
		KeyStore ks = null;
		FileInputStream fis = null;
		try {
			ks = KeyStore.getInstance("JKS");
			char[] passwordArray = password.toCharArray();
			fis = new java.io.FileInputStream(keyStoreName);
			ks.load(fis, passwordArray);
			fis.close();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally {
			if (fis != null) {
				fis.close();
			}
		}
		return ks;
	}
	
	private static SSLSocketFactory getSSLSocketFactory(String keyStoreName, String password) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, IOException {
		KeyStore ks = getKeyStore(keyStoreName, password);
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
		keyManagerFactory.init(ks, password.toCharArray());

		  SSLContext context = SSLContext.getInstance("TLS");
		  context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());

		  return context.getSocketFactory();
	}
	
	// Source - http://www.mkyong.com/java/how-to-convert-inputstream-to-string-in-java/
	private static String getStringFromInputStream(InputStream is) {
		 
		BufferedReader br = null;
		StringBuilder sb = new StringBuilder();
 
		String line;
		try {
 
			br = new BufferedReader(new InputStreamReader(is));
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
 
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
 
		return sb.toString();
 
	}
	
	private static String processGetRequest(URL url, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setRequestMethod("GET");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		InputStream responseStream = (InputStream) con.getContent();
		String response = getStringFromInputStream(responseStream);
		responseStream.close();
		return response;
	}
	
	private static int processPostRequest(URL url, byte[] data, String contentType, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = null;
		con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setDoOutput(true);
		con.setRequestMethod("POST");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		con.setRequestProperty("Content-Length", String.valueOf(data.length));
		con.setRequestProperty("Content-Type", contentType);
		
		DataOutputStream  requestStream = new DataOutputStream (con.getOutputStream());
		requestStream.write(data);
		requestStream.flush();
		requestStream.close();
		return con.getResponseCode();
	}
	
	private static int processDeleteRequest(URL url, String keyStore, String keyStorePassword) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException {
		SSLSocketFactory sslFactory = getSSLSocketFactory(keyStore, keyStorePassword);
		HttpsURLConnection con = null;
		con = (HttpsURLConnection) url.openConnection();
		con.setSSLSocketFactory(sslFactory);
		con.setRequestMethod("DELETE");
		con.addRequestProperty("x-ms-version", "2012-03-01");
		return con.getResponseCode();
	}
	
	public static void main(String[] args)
	{
		try {
			String subscriptionId = "fd9a283a-3a98-b17e-a2b8-763cf6e9ba18";
			String keyStorePath = "C:\\Program Files\\Java\\jre7\\bin\\WindowsAzureKeyStore.jks";
			String keyStorePassword = "test123";
			String url = "";

			//List locations
			url = String.format("https://management.core.windows.net/%s/locations", subscriptionId);
			String response = processGetRequest(new URL(url), keyStorePath, keyStorePassword);
			System.out.println(response);
			
			//Create cloud service
			String cloudServiceName = "cloudserviceusingjava";
			String label = "my awesome cloud service in java";
			String description = "my awesome cloud service in java";
			String location = "Southeast Asia";
			url = String.format("https://management.core.windows.net/%s/services/hostedservices", subscriptionId);
			String requestBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?><CreateHostedService xmlns=\"http://schemas.microsoft.com/windowsazure\"><ServiceName>%s</ServiceName><Label>%s</Label><Description>%s</Description><Location>%s</Location></CreateHostedService>";
			requestBody = String.format(requestBody, cloudServiceName, Base64.encodeBase64String(label.getBytes()), description, location);
			int createResponseCode = processPostRequest(new URL(url), requestBody.getBytes(), "application/xml", keyStorePath, keyStorePassword);
			System.out.println(createResponseCode);
			//Delete cloud service
			String cloudServiceToDelete = "cloudserviceusingjava";
			url = String.format("https://management.core.windows.net/%s/services/hostedservices/%s", subscriptionId, cloudServiceToDelete);
			int deleteResponseCode = processDeleteRequest(new URL(url), keyStorePath, keyStorePassword);
			System.out.println(deleteResponseCode);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Acknowledgement

As I told you above I haven’t worked with Java in ages and I had to search through many Internet sites to get the information I wanted. StackOverflow obviously had been a great help however one site I want to mention in particular is http://www.mkyong.com. This site has some really helpful blog posts on Java.

Summary

That’s it for this post. I hope you will find this information useful. As always, please let me know if I have made any mistakes and I will correct them ASAP.

Happy Coding!!!


[This is the latest product I'm working on]