Hacking Spring Initializr

We all have that person on the team who loves leave a wake of despair and destruction in the code by constantly shifting their focus from one technology to another between projects. BTW, have you seen Kafka yet? Someone who is constantly bringing you super cool patterns and new technology. Which reminds me, you need to start using React because #Facebook. That one senior technologist who keeps reinventing your persistence layer because of some internet blog post by a college student who one time, in a class project, said “Wow Amazing!”. I wonder if I can get Entity Framework to work in Java?!

I will admit to often being that person. However, I have more recently taken the approach that microservice and pattern “constriction” is actually a good thing where you are mostly using nails and hammers. This is in a bit of contast to what Fowler says below at https://martinfowler.com/articles/microservices.html

One of the consequences of centralised governance is the tendency to standardise on single technology platforms. Experience shows that this approach is constricting — not every problem is a nail and not every solution a hammer. We prefer using the right tool for the job and while monolithic applications can take advantage of different languages to a certain extent, it isn’t that common.

Within large Enterprise projects that are comprised of different language experts, tools and technologies the freedom to shoot ones own foot or make life harder for the next person to maintain your code is really high where people are allowed to run amuck on googling “best new shiny microservice thing”.

We actually faced more issues by allowing this freedom due to a very large team and lack of clear patterns and protocols when communicating in our distributed environment. We are all in with Spring and Spring Boot so we decided to make our “constrictions” meaningful and allow freedom of implementation and pattern usage instead of complete and absolute freedom in building Spring applications. Fowler does move on to say the following:

Of course, just because you can do something, doesn’t mean you should — but partitioning your system in this way means you have the option.

Marty, in that we can agree. Below is an example of the path we have chosen to walk for allowing some freedoms but still putting the correct amount of constriction on the developers.

Spring Initializr

If you develop in Java in your enterprise and you work with Spring Boot it would stand to reason that you already have heard of or use http://start.spring.io/. This is Spring Initializr. It is an amazing place to start to allow your creative juices to flow freely within the Spring ecosystem. There has been a lot of work done to make a fully capable web UI and API to deliver a pre-configured project shell with all your Spring and Spring approved libraries.

The Spring team has done a lot of work figuring out versioning and library compatibility between a lot of the key Java libraries within the Spring Platform. This is a great tool for us to start limiting the ability of our teams to create chaos by forking this project and enhancing it’s capabilities.

Get Hacking

Getting Started

There is no secret in getting started with making these changes. It simply requires modifying the core code from Initializr. We start by forking the main repo. I have done this and have all my goodies added here https://github.com/Grails-Plugin-Consortium/initializr.

I have broken the process of hacking Initializr up into 6 separate steps to illustrate the evolution into an enterprise and ops friendly process. These are by no means the limit to which anyone should stop in making Initializr more contextual for your domain needs.

Step 1 — Add Application

The first step is to add a web application that bundles all the Initializr components into a runnable boot application. This is also done so that we can inject our own application.yml with custom dependencies and have more control in running our fork.

Initializr Application Module

You can find the whole of this work at the following branch https://github.com/Grails-Plugin-Consortium/initializr/tree/step01_add-app

Step 2 — Add Profiles

We really wanted to give our developers some pre-built selections of dependencies for building different application profiles. For example we want to put together all the web, ops, metrics, jdbc, etc into a bundle for doing a microservice. However we might only want spring core and spock for doing a library. By adding the profiles we can accomplish this and give the users the ability to just select from a list of profiles and not have to explicitly think about all the dependencies and whether they are including all the right things.

Application Profiles

In the above illustration I have added 6 different profiles. One of the profiles is Blank Project where no items are pre-selected. The core of these changes are in the start.js and home.html files. The bulk of this code is as follows.

The full set of changes can be found at https://github.com/Grails-Plugin-Consortium/initializr/tree/step02_add-profiles

Step 3 — Add Dockerfile

To illustrate adding additional files to the generation process, we can add a Dockerfile and some helper scripts. This particular enhancement will be something that is used frequently if you want to enhance Initializr. We can use this pattern to add example REST controllers, JPA repositories, logback xml files and many more starter files to get people quickly running with the correct code, patterns and configurations.

Added checkbox for Dockerfile generation

You do not have to add any UI to add default files, I just wanted to illustrate how to allow the user to opt in or out of a file inclusion. The code is located here: https://github.com/Grails-Plugin-Consortium/initializr/tree/step03_add-dockerfile

Step 4 — Add Configuration

This parlays on top of the previous change where we will be adding some properties to the application.yml. This will get our application configured with our correct environment variables for container deployment.

To keep the configuration file cleaner we can add some model properties so that handlebars will only add configuration blocks where the appropriate dependencies have been added.

ProjectGenerator.java

model.put("hasJpa", originalRequest.hasStyle("data-jpa"));
model.put("hasH2", originalRequest.hasStyle("h2"));

application.yml (in template folder)

{{#hasH2}}
---
spring.jpa.hibernate.ddl-auto: create

spring.datasource:
url:
jdbc:h2:mem:DB;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driver-class-name:
org.h2.Driver
platform: h2

{{/hasH2}}
{{#hasJpa}}
---
spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.naming.implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
{{/hasJpa}}

This will now only add the configuration blocks where the user has selected the appropriate dependencies. Full changes located at: https://github.com/Grails-Plugin-Consortium/initializr/tree/step04_add-configuration

Step 5 — Add Git

Having your Initializr automatically create a GitHub, GitHub Enterprise, GitLab, etc. repository helps the user not have to think about doing this task manually and can be used to set up repository hooks, build tasks and other infrastructure setup.

Simply add a new module that contains all the workings for securely calling our source control APIs as well as pushing the code using jGit. This requires a bit of project restructuring so we can share the BasicProjectRequest.java file between modules. I created a new module and moved that one file there and included it in our projects that require that shared pojo. Full changes located at: https://github.com/Grails-Plugin-Consortium/initializr/tree/step05_add-git

Step 6 — Add Custom Libraries

As your organization creates and requires the usage of some core libraries that provide integral functions, it would be smart to add these to your base application profiles in your new shiny Initializr where appropriate. Adding new libraries to the UI is as simple as modifying our initializr-app’s application.yml and putting in a new block with these libraries. These libraries can be something you create and host internally or other 3rd party libraries not already included in the core Initializr.

- name: Custom
content:
- name: Jasypt
id: jasypt
groupId: com.github.ulisesbocchio
artifactId: jasypt-spring-boot-starter
version: 1.6
description: Property encryption support
topic: gr8conf
- name: Codahale Metrics
id: metrics
groupId: com.codahale.metrics
artifactId: metrics-core
version: 3.0.2
description: Adds support for codahale metrics
topic: gr8conf

By adding the new yml property called topic we are able to modify the UI to add css styles that match these topics which allow us to color code these. So in the block above and by adding topic: jdbc to all the jdbc driver and helper libs already in the application.yml we can give the user a visually meaningful experience where they can easily discern spring libraries from custom libraries from database libraries for example.

Visual cues for the user

I have also added tooltips to the library tags to help see descriptions when the full view is collapsed. Full changes located at: https://github.com/Grails-Plugin-Consortium/initializr/tree/step06_add-adhoc

Conclusion

It is pretty easy with a little coding elbow grease to make Initializr really shine for your organization and produce contextually meaningful and useful project templates. I hope that reading this has peaked your interest in making your own awesome additions to Initializr. It has certainly helped us achieve a level of code unity where we can easily know how each service operates and is structured. This gives us the freedom of have more product polyglots as each team isn’t operating it’s own standards and patterns (and languages) where the team would be a bunch of singular domain experts. If you run a startup and one person builds an application in Clojure and another in Ratpack and maybe a third in Elixir, good on you. In reality we don’t have these freedoms in our large corporate culture so we have chosen to bet on Spring, Spring Boot and Spring Cloud. While we want to limit some aspects of how things get started, we do this to ease the burdens of having to do the DevOps work of other unnecessary tasks. In doing so we give the developers freedom to use a huge litany of Spring and internal libraries and patterns that we know are great and won’t make the applications overly complicated.