Search code examples
sslgroovyratpack

In Ratpack, how can I configure loading configuration from an external file?


I have a Ratpack app written with the Groovy DSL. (Embedded in Java, so not a script.)

I want to load the server's SSL certificates from a config file supplied in the command line options. (The certs will directly embedded in the config, or possibly in a PEM file referenced somewhere in the config.)

For example:

java -jar httpd.jar /etc/app/sslConfig.yml

sslConfig.yml:

---
ssl:
    privateKey: file:///etc/app/privateKey.pem
    certChain: file:///etc/app/certChain.pem

I seem to have a chicken-and-egg problem using the serverConfig's facilities for reading the config file in order to configure the SslContext later in the serverConfig. The server config isn't created at the point I want to load the SslContext.

To illustrate, the DSL definition I have is something like this:

   // SSL Config POJO definition
   class SslConfig {
       String privateKey
       String certChain
       SslContext build() { /* ... */ }
   }

   // ... other declarations here...

   Path configPath = Paths.get(args[1]) // get this path from the CLI options

   ratpack {
        serverConfig {
            yaml "/defaultConfig.yaml" // Defaults defined in this resource
            yaml configPath // The user-supplied config file

            env()
            sysProps('genset-server')

            require("/ssl", SslConfig) // Map the config to a POJO

            ssl sslConfig // HOW DO I GET AN INSTANCE OF that SslConfig POJO HERE?
            baseDir BaseDir.find()
        }

        handlers {
            get { // ... 
            }
        }
   }

Possibly there is a solution to this (loading the SSL context in a later block?)

Or possibly just a better way to go about the whole thing..?


Solution

  • The solution I am currently using is this, with an addition of a #builder method to SslConfig which returns a SslContextBuilder defined using its other fields.

    ratpack {
        serverConfig {
           // Defaults defined in this resource
           yaml RatpackEntryPoint.getResource("/defaultConfig.yaml") 
    
           // Optionally load the config path passed via the configFile parameter (if not null)
            switch (configPath) {
                case ~/.*[.]ya?ml/: yaml configPath; break
                case ~/.*[.]json/: json configPath; break
                case ~/.*[.]properties/: props configPath; break
            }
    
            env()
            sysProps('genset-server')
    
            require("/ssl", SslConfig) // Map the config to a POJO
    
            baseDir BaseDir.find()
    
            // This is the important change.
            // It apparently needs to come last, because it prevents
            // later config directives working without errors
            ssl build().getAsConfigObject('/ssl',SslConfig).object.builder().build()
        }
    
        handlers {
            get { // ... 
            }
        }
    

    }

    Essentially this performs an extra build of the ServerConfig in order to redefine the input to the second build, but it works.