How to make Magnolia CMS ready for the Cloud Foundry PaaS – Milestone 2 (Persisting Data)

This is part of a project YMC is working on for Migros, Switzerland’s biggest retailer. Migros IT Services strive to operate their software in a modern, flexible, and scalable infrastructure.

In the previous article of this blog series, we deployed Magnolia CMS to the Cloud Foundry Platform as a Service. So far, this is only a proof of concept. To lift this setup to a production level and to benefit from further cloud advantages, a few more obstacles have to be solved. One of the biggest weaknesses is the lack of persistence: No locally stored content will survive rebooting.

Now the configuration of an external database in Magnolia is not really an issue, but hard-coding database credentials into packaged configuration files doesn’t sound very cloudy, does it? Way more flexibility could be obtained by injecting the database into the application: This would allow us to swap the database without touching the code. Furthermore, running multiple installations of the same application without packing additional repository configuration into the WAR-file is another advantage.

In this article I will explain a flexible and lean way to persist Magnolia content in an external database when working with Cloud Foundry.

Cloud Foundry Services

In Cloud Foundry, a database can be bound to an application as a service.

Pivotal Cloud Foundry comes with a service marketplace, where creating a new service instance and binding it to an application only takes three clicks in Pivotal’s web interface (or one command using Cloud Foundry’s CLI):

In this article I will explain a flexible and lean way to persist Magnolia content in an external database when working with Cloud Foundry.

If there is no suitable database service for your requirements in the marketplace you may even add your own “user-provided” MySQL-service.

1

2

3

4

5

6

7

8

cf create-user-provided-service author_magnolia_db-p'{

"type":"mysql",

"host":"HOST",

"port":"3306",

"name":"DB",

"username":"USER",

"password":"PW"

}'

The CLI can also bind it to an application: cf bind-service mgnl-author author_magnolia_db or alternatively the service binding can be configured in the Cloud Foundry manifest.yml:

1

2

3

4

5

6

7

8

9

10

11

12

13

---

path:webapp/target/magnolia-app.war

timeout:180

memory:2G

instances:1

env:

magnoliaInstanceType:cfauthor

applications:

-name:mgnl-author

services:

-author_magnolia_db

Accessing Cloud Foundry Services in Magnolia

Cloud Foundry services are injected into the applications with the help of the environment variable VCAP_SERVICES. This variable contains all bonded services in a JSON string:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

---

{

"app-autoscaler":[{

"...":"..."

}],

"user-provided":[{

"name":"author_magnolia_db",

"label":"user-provided",

"tags":[],

"credentials":{

"host":"HOST",

"name":"DB",

"password":"PW",

"port":"3306",

"type":"mysql",

"username":"USER"

},

"syslog_drain_url":""

}],

"p-gemfire":[{

"...":"..."

}]

}

Programmatically it is now easy to access the credentials, but Magnolia uses an XML file for database configuration where parsing a JSON string is not possible.

Therefore a little extra effort is needed: Parse VCAP_SERVICES on start up, extract the credentials and write them into separate environment variables. Fortunately Magnolia has already taken care of this by providing the Cloud Foundry Integration Module. It stores the values using the schema vcap.services.{service.label}.{instance.name}.credentials.{key}.

In our example it results in the following variables:

1

2

3

4

5

6

vcap.services.user-provided.author_magnolia_db.credentials.host

vcap.services.user-provided.author_magnolia_db.credentials.name

vcap.services.user-provided.author_magnolia_db.credentials.password

vcap.services.user-provided.author_magnolia_db.credentials.port

vcap.services.user-provided.author_magnolia_db.credentials.type

vcap.services.user-provided.author_magnolia_db.credentials.username

They can be used in the definition in Magnolia’s repository configuration like this:

1

The disadvantage of this method is the necessity to hardcode values for {service.label} and {instance.name} into the repository configuration, making it less flexible.

To achieve our goal, we have modified Magnolia’s Cloud Foundry Module to map a service which ends with “magnolia_db” to vcap.services.autodetected.magnolia_db.credentials.{key}.

This way, injecting whatever MySQL service is possible, as long as its name follows the pattern mentioned.

Conclusion

That’s it! On start up, Magnolia will connect the injected database and use it for persisting content. You only need one repository configuration for multiple Magnolia Cloud Foundry applications (e.g. one author and two public instances).

For dynamic horizontal scaling of the public instances, it would be great if we could reuse the same service for multiple instances. This is possible using a JCR Cluster, which will be covered in an upcoming article – stay tuned, we’ll announce it on Twitter soon!