VOOZH about

URL: https://www.javacodegeeks.com/2016/05/approaches-binding-spring-boot-application-service-cloud-foundry.html

⇱ Approaches to binding a Spring Boot application to a service in Cloud Foundry - Java Code Geeks


If you want to try out Cloud Foundry the simplest way to do that is to download the excellent PCF Dev or to create a trial account at the Pivotal Web Services site.

The rest of the post assumes that you have an installation of Cloud Foundry available to you and that you have a high level understanding of Cloud Foundry. The objective of this post is to list out of the options you have in integrating your Java application to a service instance – this demo uses mysql as a sample service to integrate with but the approach is generic enough.

Overview of the Application

The application is fairly simple Spring-Boot app, it is a REST service exposing three domain types and their relationships, representing a university – Course, Teacher and Student. The domain instances are persisted to a MySQL database. The entire source code and the approaches are available at this github location if you want to jump ahead.

To try the application locally, first install a local mysql server database, on a Mac OSX box with homebrew available, the following set of commands can be run:

brew install mysql

mysql.server start
mysql -u root
# on the mysql prompt: 

CREATE USER 'univadmin'@'localhost' IDENTIFIED BY 'univadmin';
CREATE DATABASE univdb;
GRANT ALL ON univdb.* TO 'univadmin'@'localhost';

Bring up the Spring-Boot under cf-db-services-sample-auto:

mvn spring-boot:run

and an endpoint with a sample data will be available at http://localhost:8080/courses.

Trying this application on Cloud Foundry

If you have an installation of PCF Dev running locally, you can try out a deployment of the application the following way:

cf api api.local.pcfdev.io --skip-ssl-validation
cf login # login with admin/admin credentials

Create a Mysql service instance:

cf create-service p-mysql 512mb mydb

and push the app! (manifest.yml provides the binding of the app to the service instance)

cf push

An endpoint should be available at http://cf-db-services-sample-auto.local.pcfdev.io/courses

Approaches to service connectivity

Now that we have an application that works locally and on a sample local Cloud Foundry, these are the approaches to connecting to a service instance.

Approach 1 – Do nothing, let the Java buildpack handle the connectivity details

This approach is demonstrated in the cf-db-services-sample-auto project. Here the connectivity to the local database has been specified using Spring Boot and looks like this:

---

spring:
 jpa:
 show-sql: true
 hibernate.ddl-auto: none
 database: MYSQL

 datasource:
 driverClassName: com.mysql.jdbc.Driver
 url: jdbc:mysql://localhost/univdb?autoReconnect=true&useSSL=false
 username: univadmin
 password: univadmin

When this application is pushed to Cloud Foundry using the Java Buildpack, a component called the java-buildpack-auto-reconfiguration is injected into the application which reconfigures the connectivity to the service based on the runtime service binding.

Approach 2 – Disable Auto reconfiguration and use runtime properties

This approach is demonstrated in the cf-db-services-sample-props project. When a service is bound to an application, there is a set of environment properties injected into the application under the key “VCAP_SERVICES”. For this specific service the entry looks something along these lines:

"VCAP_SERVICES": {
 "p-mysql": [
 {
 "credentials": {
 "hostname": "mysql.local.pcfdev.io",
 "jdbcUrl": "jdbc:mysql://mysql.local.pcfdev.io:3306/cf_456d9e1e_e31e_43bc_8e94_f8793dffdad5?user=**\u0026password=***",
 "name": "cf_456d9e1e_e31e_43bc_8e94_f8793dffdad5",
 "password": "***",
 "port": 3306,
 "uri": "mysql://***:***@mysql.local.pcfdev.io:3306/cf_456d9e1e_e31e_43bc_8e94_f8793dffdad5?reconnect=true",
 "username": "***"
 },
 "label": "p-mysql",
 "name": "mydb",
 "plan": "512mb",
 "provider": null,
 "syslog_drain_url": null,
 "tags": [
 "mysql"
 ]
 }
 ]
 }

The raw json is a little unwieldy to consume, however Spring Boot automatically converts this data into a flat set of properties that looks like this:

"vcap.services.mydb.plan": "512mb",
"vcap.services.mydb.credentials.username": "******",
"vcap.services.mydb.credentials.port": "******",
"vcap.services.mydb.credentials.jdbcUrl": "******",
"vcap.services.mydb.credentials.hostname": "******",
"vcap.services.mydb.tags[0]": "mysql",
"vcap.services.mydb.credentials.uri": "******",
"vcap.services.mydb.tags": "mysql",
"vcap.services.mydb.credentials.name": "******",
"vcap.services.mydb.label": "p-mysql",
"vcap.services.mydb.syslog_drain_url": "",
"vcap.services.mydb.provider": "",
"vcap.services.mydb.credentials.password": "******",
"vcap.services.mydb.name": "mydb",

Given this, the connectivity to the database can be specified in a Spring Boot application the following way – in a application.yml file:

spring:
 datasource:
 url: ${vcap.services.mydb.credentials.jdbcUrl}
 username: ${vcap.services.mydb.credentials.username}
 password: ${vcap.services.mydb.credentials.password}

One small catch though is that since I am now explicitly taking control of specifying the service connectivity, the runtime java-buildpack-auto-reconfiguration has to be disabled, which can done by a manifest metadata:

---
applications:
 - name: cf-db-services-sample-props
 path: target/cf-db-services-sample-props-1.0.0.RELEASE.jar
 memory: 512M
 env:
 JAVA_OPTS: -Djava.security.egd=file:/dev/./urandom
 SPRING_PROFILES_ACTIVE: cloud
 services:
 - mydb

buildpack: https://github.com/cloudfoundry/java-buildpack.git

env:
 JBP_CONFIG_SPRING_AUTO_RECONFIGURATION: '{enabled: false}'

Approach 3 – Using Spring Cloud Connectors

The third approach is to use the excellent Spring Cloud Connectors project and a configuration which specifies a service connectivity looks like this and is demonstrated in the cf-db-services-sample-connector sub-project:

@Configuration
@Profile("cloud")
public class CloudFoundryDatabaseConfig {

 @Bean
 public Cloud cloud() {
 return new CloudFactory().getCloud();
 }

 @Bean
 public DataSource dataSource() {
 DataSource dataSource = cloud().getServiceConnector("mydb", DataSource.class, null);
 return dataSource;
 }
}

Pros and Cons

These are the Pros and Cons with each of these approaches:

ApproachesProsCons
Approach 1 – Let Buildpack handle it1. Simple, the application that works locally will work without any changes on the cloud1. Magical – the auto-reconfiguration may appear magical to someone who does not understand the underlying flow
2. The number of service types supported is fairly limited –
say for eg, if a connectivity is required to Cassandra then Auto-reconfiguration will not work
Approach 2 – Explicit Properties1. Fairly straightforward.
2. Follows the Spring Boot approach and uses some of the best practices of Boot based applications – for eg, there is a certain order in which datasource connection pools are created, all those best practices just flow in using this approach.
1. The Auto-reconfiguration will have to be explicitly disabled
2. Need to know what the flattened properties look like
3. A “cloud” profile may have to be manually injected through environment properties to differentiate local development and cloud deployment
4. Difficult to encapsulate reusability of connectivity to newer service types – say Cassandra or DynamoDB.
Approach 3 – Spring Cloud Connectors1. Simple to integrate
2. Easy to add in re-usable integration to newer service types
1. Bypasses the optimizations of Spring Boot connection pool logic.

Conclusion

My personal preference is to go with Approach 2 as it most closely matches the Spring Boot defaults, not withstanding the cons of the approach. If more complicated connectivity to a service is required I will likely go with approach 3. Your mileage may vary though

References

1. Scott Frederick‘s spring-music has been a constant guide.

2. I have generously borrowed from Ben Hale‘s pong_matcher_spring sample.

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Thank you!

We will contact you soon.

👁 Photo of Biju Kunjummen
Biju Kunjummen
May 5th, 2016Last Updated: May 3rd, 2016
2 630 4 minutes read
Subscribe

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Miguel Salinas Gancedo
7 years ago

The first on Good tutorial, but I have a question:

I used the second option, disable the autoconfiguration Buildpack and get the VCAP_SERVICES envirotment variable to configure Spring Boot conection. But I made a small trap, that is: in the Spring Boot configuration:

datasource:
driverClassName: com.mysql.jdbc.Driver
url: ${vcap.services.mydb.credentials.jdbcUrl}
username: ${vcap.services.mydb.credentials.username}
password: xxx

The App deply correctly and works correctly the MySQL service, how could it be possible??, the password is wrong!!

Regards

0
Reply
Jon Britton
6 years ago

The password is probably also included in the jdbcUrl

0
Reply
Back to top button
Close
wpDiscuz