Trying Out Microsoft’s Spring Boot Starters

Do you build Java apps? If so, there’s a good chance you’re using Spring Boot. Web apps, event-driven apps, data processing apps, you name it, Spring Boot has libraries that help. It’s pretty great. I’m biased; I work for the company that maintains Spring, and taught two Pluralsight courses about Spring Cloud. But there’s no denying the momentum:

If a platform matters, it works with Spring Boot. Add Microsoft Azure to the list. Microsoft and Pivotal engineers created some Spring Boot “starters” for key Azure services. Starters make it simple to add jars to your classpath. Then, Spring Boot handles the messy dependency management for you. And with built-in auto-configuration, objects get instantiated and configured automatically. Let’s see this in action. I built a simple Spring Boot app that uses these starters to interact with Azure Storage and Azure DocumentDB (CosmosDB). 

I started at Josh Long’s second favorite place on the Internet: start.spring.io. Here, you can bootstrap a new project with all sorts of interesting dependencies, including Azure! I defined my app’s group and artifact IDs, and then chose three dependencies: web, Azure Storage, and Azure Support. “Azure Storage” brings the jars in for storage, and “Azure Support” activates other Azure services when you reference their jars.

2017.11.02-boot-01

I downloaded the resulting project and opened it in Spring Tool Suite. Then I added one new starter to my Maven POM file:

<dependency>
	<groupId>com.microsoft.azure</groupId>
	<artifactId>azure-documentdb-spring-boot-starter</artifactId>
</dependency>

That’s it. From these starters, Spring Boot pulls in everything our app needs. Next, it was time for code. This basic REST service serves up product recommendations. I wanted to store each request for recommendations as a log file in Azure Storage and a record in DocumentDB. I first modeled a “recommendations” that goes into DocumentDB. Notice the topmost annotation and reference to a collection.

package seroter.demo.bootazurewebapp;
import com.microsoft.azure.spring.data.documentdb.core.mapping.Document;

@Document(collection="items")
public class RecommendationItem {

	private String recId;
	private String cartId;
	private String recommendedProduct;
	private String recommendationDate;
	public String getCartId() {
		return cartId;
	}
	public void setCartId(String cartId) {
		this.cartId = cartId;
	}
	public String getRecommendedProduct() {
		return recommendedProduct;
	}
	public void setRecommendedProduct(String recommendedProduct) {
		this.recommendedProduct = recommendedProduct;
	}
	public String getRecommendationDate() {
		return recommendationDate;
	}
	public void setRecommendationDate(String recommendationDate) {
		this.recommendationDate = recommendationDate;
	}
	public String getRecId() {
		return recId;
	}
	public void setRecId(String recId) {
		this.recId = recId;
	}
}

Next I defined an interface that extends DocumentDbRepository.

package seroter.demo.bootazurewebapp;
import org.springframework.stereotype.Repository;
import com.microsoft.azure.spring.data.documentdb.repository.DocumentDbRepository;
import seroter.demo.bootazurewebapp.RecommendationItem;

@Repository
public interface RecommendationRepo extends DocumentDbRepository<RecommendationItem, String> {
}

Finally, I build the REST handler that talks to Azure Storage and Azure DocumentDB.  Note a few things. First, I have a pair of autowired variables. These reference beans created by Spring Boot and injected at runtime. In my case, they should be objects that are already authenticated with Azure and ready to go.

@RestController
@SpringBootApplication
public class BootAzureWebAppApplication {
	public static void main(String[] args) {
	  SpringApplication.run(BootAzureWebAppApplication.class, args);
	}

	//for blob storage
	@Autowired
	private CloudStorageAccount account;

	//for Cosmos DB
	@Autowired
	private RecommendationRepo repo;

In the method that actually handles the HTTP POST, I first referenced the Azure Storage Blob containers and add a file there. I got to use the autowired CloudStorageAccount here. Next, I created a RecommendationItem object and loaded it into the autowired DocumentDB repo. Finally, I returned a message to the caller.

@RequestMapping(value="/recommendations", method=RequestMethod.POST)
public String GetRecommendedProduct(@RequestParam("cartId") String cartId) throws URISyntaxException, StorageException, IOException {

	//create log file and upload to an Azure Storage Blob
	CloudBlobClient client = account.createCloudBlobClient();
	CloudBlobContainer container = client.getContainerReference("logs");
	container.createIfNotExists();

	String id = UUID.randomUUID().toString();
	String logId = String.format("log - %s.txt", id);
	CloudBlockBlob blob = container.getBlockBlobReference(logId);
	//create the log file and populate with cart id
	blob.uploadText(cartId);

	//add to DocumentDB collection (doesn't have to exist already)
	RecommendationItem r = new RecommendationItem();
	r.setRecId(id);
	r.setCartId(cartId);
	r.setRecommendedProduct("Y777-TF2001");
	r.setRecommendationDate(new Date().toString());
	repo.save(r);

	return "Happy Fun Ball (Y777-TF2001)";
}

Excellent. Next up, creating the actual Azure services! From the Azure Portal, I created a new Resource Group called “boot-demos.” This holds all the assets related to this effort. I then added an Azure Storage account to hold my blobs.

2017.11.02-boot-02

Next, I grabbed the connection string to my storage account.

2017.11.02-boot-03

I took that value, and added it to the application.properties file in my Spring Boot app.

azure.storage.connection-string=DefaultEndpointsProtocol=https;AccountName=bootapplogs;AccountKey=[KEY VALUE];EndpointSuffix=core.windows.net

Since I’m also using DocumentDB (part of CosmosDB), I needed an instance of that as well.

2017.11.02-boot-04

Can you guess what’s next? Yes, it’s credentials. Specifically, I needed the URI and primary key associated with my Cosmos DB account.

2017.11.02-boot-05

I snagged those values and also put them into my application.properties file.

azure.documentdb.database=recommendations
azure.documentdb.key=[KEY VALUE]
azure.documentdb.uri=https://bootappdocs.documents.azure.com:443/

That’s it. Those credentials get used when activating the Azure beans, and my code gets access to pre-configured objects. After starting up the app, I sent in a POST request.

2017.11.02-boot-06

I got back a “recommended product”, but more importantly, I didn’t get an error! When I looked back at the Azure Portal, I saw two things. First, I saw a new log file in my newly created blob container.

2017.11.02-boot-07

Secondly, I saw a new database and document in my Cosmos DB account.

2017.11.02-boot-08

That was easy. Spring Boot apps, consuming Microsoft Azure services with no fuss.

Note that I let my app automatically create the Blob container and DocumentDB database. In real life you might want to create those ahead of time in order to set various properties and not rely on default values.

Bonus Demo – Running this app in Cloud Foundry

Let’s not stop there. While the above process was simple, it can be simpler. What if I don’t want to go to Azure to pre-provision resources? And what if I don’t want to manage credentials in my application itself? Fret not. That’s where the Service Broker comes in.

Microsoft created an Azure Service Broker for Cloud Foundry that takes care of provisioning resources and attaching those resources to apps. I added that Service Broker to my Pivotal Web Services (hosted Cloud Foundry) account.

2017.11.02-boot-09

When creating a service instance via the Broker, I needed to provide a few parameters in a JSON file. For the Azure Storage account, it’s just the (existing or new) resource group, account name, location, and type.

{
  "resourceGroup": "generated-boot-demo",
  "storageAccountName": "generatedbootapplogs",
  "location": "westus",
  "accountType": "Standard_LRS"
}

For DocumentDB, my JSON file called out the resource group, account name, database name, and location.

{
  "resourceGroup": "generated-boot-demo",
  "docDbAccountName": "generatedbootappdocs",
  "docDbName": "recommendations",
  "location": "westus"
}

Sweet. Now to create the services. It’s just a single command for each service.

cf create-service azure-documentdb standard bootdocdb -c broker-documentdb-config.json

cf create-service azure-storage standard bootstorage -c broker-storage-config.json

To prove it worked, I snuck a peek at the Azure Portal, and saw my two new accounts.

2017.11.02-boot-10

Finally, I removed all the credentials from the application.properties file, packaged my app into a jar file, and added a Cloud Foundry manifest. This manifest tells Cloud Foundry where to find the deployable asset, and which service(s) to attach to. Note that I’m referencing the ones I just created.

---
applications:
- name: boot-azure-web-app
  memory: 1G
  instances: 1
  path: target/boot-azure-web-app-0.0.1-SNAPSHOT.jar
  services:
  - bootdocdb
  - bootstorage

With that, I ran a “cf push” and the app was deployed and started up by Cloud Foundry. I saw that it was successfully bound to each service, and the credentials for each Azure service were added to the environment variables. What’s awesome is that the Azure Spring Boot Starters know how to read these environment variables. No more credentials in my application package. My environments variables for this app in Cloud Foundry are shown here.

2017.11.02-boot-11

I called my service running in Cloud Foundry, and as before, I got a log file in Blob storage and a document in Document DB.

These Spring Boot Starters offer a great way to add Azure services to your apps. They work like any other Spring Boot Starter, and also have handy Cloud Foundry helpers to make deployment of those apps super easy. Keep an eye on Microsoft’s GitHub repo for these starters. More good stuff coming.

Advertisements


Categories: Cloud, Cloud Foundry, Microsoft Azure, Spring

1 reply

Trackbacks

  1. Azure Weekly: Nov 6, 2017 – Build Azure

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: