Spring Boot and Webjars – overwriting configuration during runtime

In a previous post, I highlighted how we use Gradle to create content jars that can be used in your Spring Boot application.

In this post, I will show you how to overwrite individual files within your content jar to let your server provide a custom configuration.

Let’s say you have created a jar file called myapp.jar. It packages a configuration file using AngularJS constants. However, when you are running your Spring Boot application, you want to provide your own set of values to point at a different server or change timeout values for production.

You could add a complete template engine like Thymeleaf or the Groovy Template Engine. This seems like the right fit for more complex configuration settings. But it is just overkill when all we really want to do is just replace one static file endpoint with another.

Instead, we can provide the SimpleUrlHandlerMapping resource mapping mechanism to simply swap out the file in our jar file with one found on the server.

Say we have a settings.js file in our content jar that provides default definitions. Let’s create a file structure under src/main/resources to provide alternate configurations:

├── aws
│   └── scripts
│       └── settings.js
├── cloudbees
│   └── scripts
│       └── settings.js
└── heroku
    └── scripts
        └── settings.js

We can then add a configuration to overwrite the /scripts/settings.js location with the one in /heroku/scripts/settings.js


@Configuration
class SettingsConfiguration {

  @Value('${currentStack:test}')
  String stack

  @Bean
  SimpleUrlHandlerMapping settingsHandlerMapping() {
    SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping()
    mapping.setOrder(Integer.MIN_VALUE)
    mapping.setUrlMap(Collections.singletonMap("scripts/settings.js", settingsRequestHandler()))
    mapping
  }

  @Bean
  ResourceHttpRequestHandler settingsRequestHandler() {
    ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler()
    requestHandler.setLocations(Arrays
      .<Resource> asList(new ClassPathResource("/${stack}/"))) // <----- rewrite
    requestHandler
  }
}

With the rewrite logic in place, we can then deploy our fat jar in different services and serve out different service configurations based on the stack variable.

Using this approach, we can keep our configuration files in Javascript instead of a messy combination of template languages and externalized property files.

Leave a comment