Bilbo Baggins Bilbo Baggins - 7 months ago 63
SQL Question

How does Spring JPA on Pivotal Cloud Foundry know how to connect to a bound MySQL instance?

I have a small API running on PCF using Spring JPA. Of course, within the code, I could use a JDBC connection running prepared statements to access a bound MySQL instance. Doing this requires a username and password, as per normal standards when connecting to a database via Java.

However, with Spring JPA, I don't have to do any of this. I simply bind the MySQL instance and can perform my queries using the JPA API.

For lack of a better question, what is this magic?

Answer

Cloudfoundry with Spring Cloud follows 12-factor app patterns through out.

For configuration also it uses the config pattern suggested by 12-factor app patterns.

According to this pattern we should be storing properties outside the code in the environment as environment variables. So that application bundle can be deployed to any environment once it's built without any modifications. Since it picks up configuration from the environment variables, different environments have to define same environment variables with the different values.

Whenever you add a service to your application using cf bind-service Cloudfoundry sets predefined environment variables related to that service in the virtual machine (or container or whatever it has).

You can check these environment variables using cf env app-name.(Command Refeference)

Sample output of cf env app-name

{
 "VCAP_APPLICATION": {
  "application_id": "fa05c1a9-0fc1-4fbd-bae1-139850dec7a3",
  "application_name": "my-app",
  "application_uris": [
    "my-app.10.244.0.34.xip.io"
  ],
  "application_version": "fb8fbcc6-8d58-479e-bcc7-3b4ce5a7f0ca",
  "limits": {
  "disk": 1024,
  "fds": 16384,
  "mem": 256
  },
  "name": "my-app",
  "space_id": "06450c72-4669-4dc6-8096-45f9777db68a",
  "space_name": "my-space",
  "uris": [
    "my-app.10.244.0.34.xip.io"
  ],
  "users": null,
  "version": "fb8fbcc6-8d58-479e-bcc7-3b4ce5a7f0ca"
}

Using the spring actuator endpoints you can inspect all environment variables using /env endpoint. It lists more properties than cf env.

When spring detects that

  1. cloud profile is active (set by spring.profiles.active environment property, or spring.profile property in spring cloud)
  2. Auto Configuration is enabled (enabled by @SpringBootApplication)
  3. No in memory Datasource dependency is present on the classpath (though I assume it would give cloud datasource configuration preference, even if in memory dependency were present)
  4. No data source has been explicitly configured

Spring creates the Datasource bean itself using environment variables if a datasource service (like Postgres) has been bound to application.

Below is the link for the environment properties that it uses for creating Datasource.

https://docs.cloudfoundry.org/buildpacks/java/spring-service-bindings.html

Here is a list of Datasource only properties.

cloud.services.<database-service-name>.connection.hostname
cloud.services.<database-service-name>.connection.name
cloud.services.<database-service-name>.connection.password
cloud.services.<database-service-name>.connection.port
cloud.services.<database-service-name>.connection.username
cloud.services.<database-service-name>.plan
cloud.services.<database-service-name>.type

database-service-name is defined in the Manifest.yml file in the env: block

In my experience if there's only one database service added to the application, there was no need to define the database service name in the environment variables section.

Note:

By default spring would try to use the servlet container's poolable connection support, however most of the time we our self have to configure some properties that are only supported by connection pool providers like Apache DBCP. In these cases we have to create Datasource bean manually using environment properties (using System.getProperty() or spring Environment.getProperty()).

Comments