Search

Dark theme | Light theme
Showing posts with label Spring:Sweets. Show all posts
Showing posts with label Spring:Sweets. Show all posts

October 10, 2024

Spring Sweets: Using Duration Type With Configuration Properties

With @ConfigurationProperties in Spring Boot we can bind configuration properties to Java classes. The class annotated with @ConfigurationProperties can be injected into other classes and used in our code. We can use the type Duration to configure properties that express a duration. When we set the value of the property we can use:

  • a long value with the unit to express milliseconds,
  • a value following the ISO-8601 duration format,
  • a special format supported by Spring Boot with the value and unit.

We can also use the @DurationUnit annotation to specify the unit for a long value. So instead of the default milliseconds we can specify the unit to be seconds or minutes for example. The unit is defined by the java.time.temporal.ChronoUnit enum and we pass it as an argument to the annotation.

The special format supported by Spring Boot supports the following units:

  • ns for nanoseconds,
  • us for microseconds,
  • ms for milliseconds,
  • s for seconds,
  • m for minutes,
  • h for hours,
  • d for days.

In the following example we define a record TimeoutProperties annotated with @ConfigurationProperties and four properties of type Duration. The property idleTimeout has the @DurationUnit annotation to specify the unit to be seconds.

package mrhaki;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

@ConfigurationProperties(prefix = "timeout")
public record TimeoutProperties(
    Duration connectionTimeout,
    Duration readTimeout,
    Duration writeTimeout,
    @DurationUnit(ChronoUnit.SECONDS) Duration idleTimeout
) {}

In our application.properties file we can set the values of the properties:

# long value (in milliseconds)
timeout.connection-timeout=5000

# ISO-8601 format
timeout.read-timeout=PT30S

# Spring Boot's format
timeout.write-timeout=1m

# value in seconds (due to @DurationUnit annotation)
timeout.idle-timeout=300

In the following test we test the values of the properties in the class TimeoutProperties.

package mrhaki;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@TestPropertySource(properties = {
    "timeout.connection-timeout=5000", // use long value in milliseconds
    "timeout.read-timeout=PT30S", // use ISO-8601 duration format
    "timeout.write-timeout=1m", // use special format supported by Spring Boot
    "timeout.idle-timeout=300" // use long value in seconds (set by @DurationUnit)
})
class TimeoutPropertiesTest {

    @Autowired
    private TimeoutProperties timeoutProperties;

    @Test
    void testConnectionTimeout() {
        assertThat(timeoutProperties.connectionTimeout())
            .isEqualTo(Duration.ofMillis(5000))
            .isEqualTo(Duration.ofSeconds(5));
    }

    @Test
    void testReadTimeout() {
        assertThat(timeoutProperties.readTimeout())
            .isEqualTo(Duration.ofSeconds(30));
    }

    @Test
    void testWriteTimeout() {
        assertThat(timeoutProperties.writeTimeout())
            .isEqualTo(Duration.ofMinutes(1))
            .isEqualTo(Duration.ofSeconds(60));
    }


    @Test
    void testIdleTimeout() {
        assertThat(timeoutProperties.idleTimeout())
            .isEqualTo(Duration.ofSeconds(300))
            .isEqualTo(Duration.ofMinutes(5));
    }

    @Test
    void testTimeoutToMillis() {
        assertThat(timeoutProperties.connectionTimeout().toMillis()).isEqualTo(5000);
        assertThat(timeoutProperties.readTimeout().toMillis()).isEqualTo(30000);
        assertThat(timeoutProperties.writeTimeout().toMillis()).isEqualTo(60000);
        assertThat(timeoutProperties.idleTimeout().toMillis()).isEqualTo(300000);
    }
}

Written with Spring Boot 3.4.4.

October 6, 2024

Spring Sweets: Using Configuration Properties With DataSize

With @ConfigurationProperties in Spring Boot we can bind configuration properties to Java classes. The class annotated with @ConfigurationProperties can be injected into other classes and used in our code. We can use the type DataSize to configure properties that express a size in bytes. When we set the value of the property we can use a long value. The size is then in bytes as that is the default unit. We can also add a unit to the value. Valid units are B for bytes, KB for kilobytes, MB for megabytes, GB for gigabytes and TB for terabytes.

We can also use the @DataSizeUnit annotation to specify the unit of the property in our class annotated with @ConfigurationProperties. In that case a the value without a unit assigned to the property is already in the specified unit.

In the following example we define a record StorageProperties annotated with @ConfigurationProperties and two properties maxFileSize and cacheSize. The property maxFileSize has no unit assigned to the value so it will be in the default unit of bytes. The property cacheSize has the unit MB assigned so the value will be in megabytes.

package mrhaki;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

@ConfigurationProperties(prefix = "storage")
public record StorageProperties(
    DataSize maxFileSize /* Default unit is bytes */,
    @DataSizeUnit(DataUnit.MEGABYTES) DataSize cacheSize /* Explicit unit is megabytes */
) {}

In the following test we test the values of the properties in the class StorageProperties. We set the value of the maxFileSize property to 10MB with an explicit unit and the value of the cacheSize property without an explicit unit. We also test the values of the properties in bytes.

package mrhaki;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@TestPropertySource(properties = {
    "storage.max-file-size=10MB", // value with explicit unit
    "storage.cache-size=500" // value without explicit unit
})
class StoragePropertiesTest {

    @Autowired
    private StorageProperties storageProperties;

    @Test
    void testMaxFileSize() {
        assertThat(storageProperties.maxFileSize())
            .isNotNull()
            // The DataSize class has useful static methods
            // to get values for a certain unit.
            .isEqualTo(DataSize.ofMegabytes(10));
    }

    @Test
    void testCacheSize() {
        assertThat(storageProperties.cacheSize())
            .isNotNull()
            .isEqualTo(DataSize.of(500, DataUnit.MEGABYTES));
    }

    @Test
    void testValuesInBytes() {
        // We can use the toBytes() method to get the actual bytes.
        assertThat(storageProperties.maxFileSize().toBytes())
            .isEqualTo(10 * 1024 * 1024);
        assertThat(storageProperties.cacheSize().toBytes())
            .isEqualTo(500 * 1024 * 1024);
    }
}

Written with Spring Boot 3.3.4.

November 16, 2023

Spring Sweets: Spring Boot 3 With Gradle In IntelliJ

Spring Boot 3 requires at least Java 17, but that also means the Java version used by Gradle must also be at least 17. Otherwise we will get the following error message when we build our Spring Boot project in IntelliJ using Gradle:

A problem occurred configuring root project 'springboot'.
> Could not resolve all files for configuration ':classpath'.
   > Could not resolve org.springframework.boot:spring-boot-gradle-plugin:3.1.5.
     Required by:
         project : > org.springframework.boot:org.springframework.boot.gradle.plugin:3.1.5
      > No matching variant of org.springframework.boot:spring-boot-gradle-plugin:3.1.5 was found. The consumer was configured to find a library for use during runtime, compatible with Java 11, packaged as a jar, and its dependencies declared externally, as well as attribute 'org.gradle.plugin.api-version' with value '8.4' but:
          - Variant 'apiElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component for use during compile-time, compatible with Java 17 and the consumer needed a component for use during runtime, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'javadocElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a component for use during runtime, and its dependencies declared externally:
              - Incompatible because this component declares documentation and the consumer needed a library
              - Other compatible attributes:
                  - Doesn't say anything about its target Java version (required compatibility with Java 11)
                  - Doesn't say anything about its elements (required them packaged as a jar)
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'mavenOptionalApiElements' capability org.springframework.boot:spring-boot-gradle-plugin-maven-optional:3.1.5 declares a library, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component for use during compile-time, compatible with Java 17 and the consumer needed a component for use during runtime, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'mavenOptionalRuntimeElements' capability org.springframework.boot:spring-boot-gradle-plugin-maven-optional:3.1.5 declares a library for use during runtime, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component, compatible with Java 17 and the consumer needed a component, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'runtimeElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a library for use during runtime, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares a component, compatible with Java 17 and the consumer needed a component, compatible with Java 11
              - Other compatible attribute:
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')
          - Variant 'sourcesElements' capability org.springframework.boot:spring-boot-gradle-plugin:3.1.5 declares a component for use during runtime, and its dependencies declared externally:
              - Incompatible because this component declares documentation and the consumer needed a library
              - Other compatible attributes:
                  - Doesn't say anything about its target Java version (required compatibility with Java 11)
                  - Doesn't say anything about its elements (required them packaged as a jar)
                  - Doesn't say anything about org.gradle.plugin.api-version (required '8.4')

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

The issue is that the Spring Boot Gradle plugin 3.1.5 requires Java 17, but our project is using Java 11. We can fix this by explicitly setting the Java version that Gradle uses in IntelliJ. Go to Settings > Build, Execution, Deployment > Build Tools > Gradle and change the JVM used for Gradle to a JDK version of at least version 17.

Written with Spring Boot 3.1.5 and IntelliJ 2023.2.4.

February 15, 2019

Spring Sweets: Group Loggers With Logical Name

Spring Boot 2.1 introduced log groups. A log group is a logical name for one or more loggers. We can define log groups in our application configuration. Then we can set the log level for a group, so all loggers in the group will get the same log level. This can be very useful to change a log level for multiple loggers that belong together with one setting. Spring Boot already provides two log groups by default: web and sql. In the following list we see which loggers are part of the default log groups:

  • web: org.springframework.core.codec, org.springframework.http, org.springframework.web, org.springframework.boot.actuate.endpoint.web, org.springframework.boot.web.servlet.ServletContextInitializerBeans
  • sql: org.springframework.jdbc.core, org.hibernate.SQL

To define our own log group we must add in our application configuration the key logging.group. followed by our log group name. Next we assign all loggers we want to be part of the group. Once we have defined our group we can set the log level using the group name prefixed with the configuration key logging.level..

In the following example configuration we define a new group controllers that consists of two loggers from different packages. We set the log level for this group to DEBUG. We also set the log level of the default group web to DEBUG:

# src/main/resources/application.properties

# Define a new log group controllers.
logging.group.controllers=mrhaki.hello.HelloController, mrhaki.sample.SampleController

# Set log level to DEBUG for group controllers.
# This means the log level for the loggers
# mrhaki.hello.HelloController and mrhaki.sample.SampleController
# are set to DEBUG.
logging.level.controllers=DEBUG

# Set log level for default group web to DEBUG.
logging.level.web=DEBUG

Written with Spring Boot 2.1.3.RELEASE

April 12, 2017

Spring Sweets: Hiding Sensitive Environment Or Configuration Values From Actuator Endpoints

We can use Spring Boot Actuator to add endpoints to our application that can expose information about our application. For example we can request the /env endpoint to see which Spring environment properties are available. Or use /configprops to see the values of properties defined using @ConfigurationProperties. Sensitive information like passwords and keys are replaced with ******. Spring Boot Actuator has a list of properties that have sensitive information and therefore should be replaced with ******. The default list of keys that have their value hidden is defined as password,secret,key,token,.*credentials.*,vcap_services. A value is either what the property name ends with or a regular expression. We can define our own list of property names from which the values should be hidden or sanitized and replaced with ******. We define the key we want to be hidden using the application properties endpoints.env.keys-to-sanatize and endpoints.configprops.keys-to-sanatize.

In the following example Spring application YAML configuration we define new values for keys we want to be sanitized. Properties in our Spring environment that end with username or password should be sanatized. For properties set via @ConfigurationProperties we want to hide values for keys that end with port and key:

# File: src/main/resources/application.yml
endpoints:
  env:
    # Hide properties that end with password and username:
    keys-to-sanitize: password,username
  configprops:
    # Also hide port and key values from the output:
    keys-to-sanitize: port,key
---
# Extra properties will be exposed
# via /env endpoint.
sample:
  username: test
  password: test

When we request the /env we see in the output that values of properties that end with username and password are hidden:

...
    "applicationConfig: [classpath:/application.yml]": {
        ...
        "sample.password": "******",
        "sample.username": "******"
    },
...

When we request the /configprops we see in the output that for example key and port properties are sanitized:

...
    "spring.metrics.export-org.springframework.boot.actuate.metrics.export.MetricExportProperties": {
        "prefix": "spring.metrics.export",
        "properties": {
            ...
            "redis": {
                "key": "******",
                "prefix": "spring.metrics.application.f2325e314fc8223e6bb8ee6ddebbbd79"
            },
            "statsd": {
                "host": null,
                "port": "******",
                "prefix": null
            }
        }
    },
...

Written with Spring Boot 1.5.2.RELEASE.

March 23, 2017

Ratpacked: Add Ratpack To Spring Boot Application

In a previous post we saw how we can use Spring Boot in a Ratpack application. But the integration can also be the other way around: using Ratpack in a Spring Boot application. This way we can use Ratpack's power to handle requests sent to our Spring Boot application and still use all Spring Boot features in our application. The easiest way to add Ratpack to a Spring Boot application is adding a Ratpack dependency and use the @EnableRatpack annotation. With this annotation a RatpackServer instance is created and started along with configuration options.

Let's see an example Spring Boot application with Ratpack enabled. First we add Ratpack as dependency to our Spring Boot application. In our example we also add Ratpack's Dropwizard module as dependency. We use Gradle in our example:

// File: build.gradle
plugins {
    id 'groovy'
    id 'idea'
    id 'org.springframework.boot' version '1.5.2.RELEASE'
}

repositories {
    jcenter()
}

ext {
    ratpackVersion = '1.4.5'
}
dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
    compile 'org.springframework.boot:spring-boot-devtools'
    
    // Add Ratpack for Spring Boot dependency.
    compile "io.ratpack:ratpack-spring-boot-starter:$ratpackVersion"
    // Add Dropwizard for Ratpack dependency.
    compile "io.ratpack:ratpack-dropwizard-metrics:$ratpackVersion"
    
    runtime 'ch.qos.logback:logback-classic:1.2.2'
    
    testCompile "org.spockframework:spock-core:1.0-groovy-2.4" 
}

springBoot {
    mainClass = 'mrhaki.sample.SampleApp'    
}

Now we look at our example application. We use the annotation @EnableRatpack to have Ratpack in our Spring Boot application. We add a Spring bean that implements Action<Chain>. Beans of this type are recognised by Spring Boot and are added to the Ratpack configuration. We also add a Spring bean that is a Ratpack module. This bean is also automatically added to the Ratpack configuration.

// File: src/main/java/mrhaki/sample/SampleApp.java
package mrhaki.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import ratpack.dropwizard.metrics.DropwizardMetricsConfig;
import ratpack.dropwizard.metrics.DropwizardMetricsModule;
import ratpack.func.Action;
import ratpack.handling.Chain;
import ratpack.handling.RequestLogger;
import ratpack.spring.config.EnableRatpack;
import ratpack.spring.config.RatpackServerCustomizer;

import java.time.Duration;

// Add Ratpack configuration for Spring Boot
@EnableRatpack
@EnableConfigurationProperties
@SpringBootApplication
public class SampleApp {

    /**
     * Start application.
     * 
     * @param args
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Implementation for {@link MessageService} with pirate accent.
     * 
     * @return {@link MessageService} Pirate speak.
     */
    @Bean
    MessageService pirateMessage() {
        return name -> String.format("Arr, matey %s", name);
    }

    /**
     * Create Ratpack chain to handle requests to {@code /message} endpoint.
     * 
     * @return Ratpack chain.
     */
    @Bean
    Action<Chain> messageHandler() {
        return chain -> chain
                // Add logging for requests.
                .all(RequestLogger.ncsa())
                .get("message/:name?", ctx -> {
                    final String name = ctx.getPathTokens().getOrDefault("name", "mrhaki");
                    // Use MessageService implementation added to Spring context.
                    final String message = ctx.get(MessageService.class).message(name);
                    ctx.render(message);
                });
    }

    /**
     * Configuration properties to configure {@link DropwizardMetricsModule}.
     * Properties can be set via default Spring Boot mechanism like
     * environment variables, system properties, configuration files, etc.
     * 
     * @return Configuration for {@link DropwizardMetricsModule}
     */
    @Bean
    MetricsProperties metricsProperties() {
        return new MetricsProperties();
    }

    /**
     * Spring beans that are {@link com.google.inject.Module} objects are
     * automatically added to Ratpack's registry.
     * 
     * @param metricsProperties Configuration for module.
     * @return Module to add Dropwizard to Ratpack.
     */
    @Bean
    DropwizardMetricsModule metricsModule(final MetricsProperties metricsProperties) {
        // Create Dropwizard configuration.
        final DropwizardMetricsConfig config = new DropwizardMetricsConfig();
        if (metricsProperties.isJmx()) {
            config.jmx();
        }
        if (metricsProperties.getSlf4j().isEnabled()) {
            config.slf4j(slf4jConfig -> slf4jConfig
                    .enable(true)
                    .reporterInterval(Duration.ofSeconds(metricsProperties.getSlf4j().getInterval())));
        }

        // Create Dropwizard module.
        final DropwizardMetricsModule metricsModule = new DropwizardMetricsModule();
        metricsModule.setConfig(config);

        return metricsModule;
    }
}

We create a bean pirateMessage that implements the following interface:

package mrhaki.sample;

public interface MessageService {
    String message(final String name);
}

We also need the supporting class MetricsProperties to allow for configuration of the Dropwizard module.

// File: src/main/java/mrhaki/sample/MetricsProperties.java
package mrhaki.sample;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * Configuration for {@link ratpack.dropwizard.metrics.DropwizardMetricsModule}.
 */
@ConfigurationProperties(prefix = "dropwizard")
public class MetricsProperties {
    private boolean jmx;
    private Slf4Config slf4j = new Slf4Config();

    public boolean isJmx() {
        return jmx;
    }

    public void setJmx(final boolean jmx) {
        this.jmx = jmx;
    }

    public Slf4Config getSlf4j() {
        return slf4j;
    }

    public static class Slf4Config {
        private boolean enabled;
        private long interval = 30;

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(final boolean enabled) {
            this.enabled = enabled;
        }

        public long getInterval() {
            return interval;
        }

        public void setInterval(final long interval) {
            this.interval = interval;
        }
    }
}

To complete our application we also add a configuration file where we can change several aspects of our application:

# File: src/main/resources/application.yml
ratpack:
  port: 9000
---
dropwizard:
  jmx: true
  slf4j:
    enabled: true
    interval: 10

Let's start the application and we can see already in the logging output Ratpack is started:

$ ./gradlew bootRun
:compileJava
:compileGroovy NO-SOURCE
:processResources UP-TO-DATE
:classes
:findMainClass
:bootRun
06:38:45.137 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
06:38:45.139 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-starter/target/classes/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot/target/classes/, /spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/]
06:38:45.140 [main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/springboot/build/classes/main/, file:/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/springboot/build/resources/main/]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.2.RELEASE)

2017-03-23 06:38:45.531  INFO 25302 --- [  restartedMain] mrhaki.sample.SampleApp                  : Starting SampleApp on mrhaki-laptop-2015.fritz.box with PID 25302 (/Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/springboot/build/classes/main started by mrhaki in /Users/mrhaki/Projects/mrhaki.com/blog/posts/samples/ratpack/springboot)
2017-03-23 06:38:45.532  INFO 25302 --- [  restartedMain] mrhaki.sample.SampleApp                  : No active profile set, falling back to default profiles: default
2017-03-23 06:38:45.609  INFO 25302 --- [  restartedMain] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@701a7feb: startup date [Thu Mar 23 06:38:45 CET 2017]; root of context hierarchy
2017-03-23 06:38:46.023  INFO 25302 --- [  restartedMain] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2017-03-23 06:38:46.687  INFO 25302 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2017-03-23 06:38:46.714  INFO 25302 --- [  restartedMain] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-03-23 06:38:46.726  INFO 25302 --- [  restartedMain] ratpack.server.RatpackServer             : Starting server...
2017-03-23 06:38:46.963  INFO 25302 --- [  restartedMain] ratpack.server.RatpackServer             : Building registry...
2017-03-23 06:38:47.618  INFO 25302 --- [  restartedMain] ratpack.server.RatpackServer             : Initializing 1 services...
2017-03-23 06:38:47.711  INFO 25302 --- [  restartedMain] ratpack.server.RatpackServer             : Ratpack started for http://localhost:9000
2017-03-23 06:38:47.716  INFO 25302 --- [  restartedMain] mrhaki.sample.SampleApp                  : Started SampleApp in 2.556 seconds (JVM running for 2.992)

To add extra server configuration properties we must add a Spring bean that implements the ratpack.spring.config.RatpackServerCustomizer interface. The Spring Boot Ratpack configuration uses all beans found in the context that implement this interface. The interface has three methods we need to implement: getHandlers, getBindings and getServerConfig. The easiest way to implement the interface is by extending the class RatpackServerCustomizerAdapter. This class already provides empty implementations for the three methods. We only need to override the method we need in our application.

We rewrite our previous example application. We create a new class RatpackServerConfig that extends RatpackServerCustomizerAdapter. We override the method getServerConfig to set the development mode property of our Ratpack server configuration:

// File: src/main/java/mrhaki/sample/RatpackServerConfig.java
package mrhaki.sample;

import org.springframework.beans.factory.annotation.Autowired;
import ratpack.func.Action;
import ratpack.server.ServerConfigBuilder;
import ratpack.spring.config.RatpackProperties;
import ratpack.spring.config.RatpackServerCustomizerAdapter;

/**
 * Spring beans that implement {@link ratpack.spring.config.RatpackServerCustomizer}
 * interface our used for configuring Ratpack. The class
 * {@linly onk RatpackServerCustomizerAdapter} is a convenience class we can 
 * extend and only override the methods we need to.
 */
public class RatpackServerConfig extends RatpackServerCustomizerAdapter {

    /**
     * {@link RatpackProperties} configuration properties 
     * for Ratpack configuration.
     */
    private final RatpackProperties ratpack;

    public RatpackServerConfig(final RatpackProperties ratpack) {
        this.ratpack = ratpack;
    }

    /**
     * Extra configuration for the default Ratpack server configuration.
     * 
     * @return Extra server configuration.
     */
    @Override
    public Action<ServerConfigBuilder> getServerConfig() {
        return serverConfigBuilder -> serverConfigBuilder
                .development(ratpack.isDevelopment());
    }
    
}

We change SampleApp and add RatpackServerConfig as Spring bean:

// File: src/main/java/mrhaki/sample/SampleApp.java
...
    /**
     * Extra Ratpack server configuration.
     * 
     * @param ratpackProperties Properties for Ratpack server configuration.
     * @return Bean with extra Ratpack server configuration.
     */
    @Bean
    RatpackServerCustomizer ratpackServerSpec(final RatpackProperties ratpackProperties) {
        return new RatpackServerConfig(ratpackProperties);
    }
...

Another rewrite of our application could be to move all Ratpack configuration like handlers and bindings in the RatpackServerConfig class. We simply need to override the other two methods: getHandlers and getBindings. This way we have all the configuration together.

// File: src/main/java/mrhaki/sample/RatpackServerConfig.java
package mrhaki.sample;

import ratpack.dropwizard.metrics.DropwizardMetricsConfig;
import ratpack.dropwizard.metrics.DropwizardMetricsModule;
import ratpack.func.Action;
import ratpack.guice.BindingsSpec;
import ratpack.handling.Chain;
import ratpack.handling.RequestLogger;
import ratpack.server.ServerConfigBuilder;
import ratpack.spring.config.RatpackProperties;
import ratpack.spring.config.RatpackServerCustomizerAdapter;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;

/**
 * Spring beans that implement {@link ratpack.spring.config.RatpackServerCustomizer}
 * interface our used for configuring Ratpack. The class
 * {@linly onk RatpackServerCustomizerAdapter} is a convenience class we can 
 * extend and only override the methods we need to.
 */
public class RatpackServerConfig extends RatpackServerCustomizerAdapter {

    /**
     * {@link RatpackProperties} configuration properties 
     * for Ratpack configuration. 
     */
    private final RatpackProperties ratpack;

    /**
     * Configuration properties for {@link DropwizardMetricsModule}.
     */
    private final MetricsProperties metrics;

    public RatpackServerConfig(
            final RatpackProperties ratpack, 
            final MetricsProperties metrics) {
        this.ratpack = ratpack;
        this.metrics = metrics;
    }

    /**
     * Extra configuration for the default Ratpack server configuration.
     * 
     * @return Extra server configuration.
     */
    @Override
    public Action<ServerConfigBuilder> getServerConfig() {
        return serverConfigBuilder -> serverConfigBuilder
                .development(ratpack.isDevelopment());
    }

    /**
     * Configure Ratpack handlers.
     * 
     * @return List of Ratpack chain configurations.
     */
    @Override
    public List<Action<Chain>> getHandlers() {
        return Arrays.asList(messageHandler()); 
    }

    /**
     * Create Ratpack chain to handle requests to {@code /message} endpoint.
     *
     * @return Ratpack chain.
     */
    private Action<Chain> messageHandler() {
        return chain -> chain
                // Add logging for requests.
                .all(RequestLogger.ncsa())
                .get("message/:name?", ctx -> {
                    final String name = ctx.getPathTokens().getOrDefault("name", "mrhaki");
                    // Use MessageService implementation added to Spring context.
                    final String message = ctx.get(MessageService.class).message(name);
                    ctx.render(message);
                });
    }

    /**
     * Add {@link DropwizardMetricsModule} to the Ratpack bindings.
     * 
     * @return Ratpack bindings.
     */
    @Override
    public Action<BindingsSpec> getBindings() {
        return bindings -> {
            bindings.module(DropwizardMetricsModule.class, dropwizardMetricsConfig());
        };
    }

    /**
     * Configuration for {@link DropwizardMetricsModule}.
     * 
     * @return Configuration action for configuring {@link DropwizardMetricsModule}.
     */
    private Action<DropwizardMetricsConfig> dropwizardMetricsConfig() {
        return config -> {
            if (metrics.isJmx()) {
                config.jmx();
            }
            if (metrics.getSlf4j().isEnabled()) {
                config.slf4j(slf4jConfig -> slf4jConfig
                        .enable(true)
                        .reporterInterval(Duration.ofSeconds(metrics.getSlf4j().getInterval())));
            }
        };
    }
}

Written with Ratpack 1.4.5 and Spring Boot 1.5.2.RELEASE.

March 17, 2017

Spring Sweets: Access Application Arguments With ApplicationArguments Bean

When we start a Spring Boot application and pass arguments to the application, Spring Boot will capture those arguments and creates a Spring bean of type ApplicationArguments and puts it in the application context. We can use this Spring bean to access the arguments passed to the application. We could for example auto wire the bean in another bean and use the provided argument values. The ApplicationArguments interface has methods to get arguments values that are options and plain argument values. An option argument is prefixed with --, for example --format=xml is a valid option argument. If the argument value is not prefixed with -- it is a plain argument.

In the following example application we have a Spring Boot application with a Spring bean that implements CommandLineRunner. When we define the CommandLineRunner implementation we use the ApplicationArguments bean that is filled by Spring Boot:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USERS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     * 
     * @param dslContext JOOQ context to access data.
     * @param applicationArguments Bean filled by Spring Boot 
     *                             with arguments used to start the application
     * @return Bean to run at startup
     */
    @Bean
    CommandLineRunner showUsers(
            final DSLContext dslContext, 
            final ApplicationArguments applicationArguments) {
        
        return (String... arguments) -> {
            // Get arguments passed to the application  that are
            // not option arguments from ApplicationArguments bean.
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = applicationArguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (applicationArguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = applicationArguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                }
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

We have an alternative if we want to use the ApplicationArguments bean in a CommandlineRunner implementation. Spring Boot offers the ApplicationRunner interface. The interface has the method run(ApplicationArguments) we need to implement and gives us directly access to the ApplicationArguments bean. In the next example we refactor the application and use ApplicationRunner:

package mrhaki.springboot;

import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.Result;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static mrhaki.springboot.sql.Tables.USEaRS;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }

    /**
     * Find users based on user input given via application arguments.
     * Example usage: {@code java -jar sample.jar --format=csv mrhaki}
     *
     * @param dslContext JOOQ context to access data.
     * @return Bean to run at startup
     */
    @Bean
    ApplicationRunner showUsers(final DSLContext dslContext) {
        return (ApplicationArguments arguments) -> {
            // Get arguments passed to the application that are
            // not option arguments. 
            // In the following example: java -jar sample.jar mrhaki
            // mrhaki is the non option argument.
            final String name = arguments.getNonOptionArgs().get(0);
            final Result<Record1<String>> result =
                    dslContext.select(USERS.NAME)
                              .from(USERS)
                              .where(USERS.NAME.likeIgnoreCase(name))
                              .fetch();

            // Check if option argument --format is used.
            // In the following example: java -jar sample.jar --format=xml --format=csv
            // format is the option argument with two values: xml, csv.
            if (arguments.containsOption("format")) {
                // Get values for format option argument.
                final List<String> format = arguments.getOptionValues("format");
                if (format.contains("xml")) {
                    result.formatXML(System.out);
                } 
                if (format.contains("json")) {
                    result.formatJSON(System.out);
                }
                if (format.contains("html")) {
                    result.formatHTML(System.out);
                }
                if (format.contains("html")) {
                    result.formatCSV(System.out);
                }
            } else {
                result.format(System.out);
            }
        };
    }

}

Written with Spring Boot 1.5.2.RELEASE.

March 15, 2017

Spring Sweets: Custom Exit Code From Exception

When we write a Spring Boot application a lot of things are done for us. For example when an exception in the application occurs when we start our application, Spring Boot will exit the application with exit code 1. If everything goes well and the we stop the application the exit code is 0. When we use the run method of SpringApplication and an exception is not handled by our code, Spring Boot will catch it and will check if the exception implements the ExitCodeGenerator interface. The ExitCodeGenerator interface has one method getExitCode() which must return a exit code value. This value is used as input argument for the method System.exit() that is invoke by Spring Boot to stop the application.

In the following example application we write a Spring Boot command line application that can throw an exception on startup. The exception class implements the ExitCodeGenerator interface:

// File: src/main/java/mrhaki/springboot/SampleApp.java
package mrhaki.springboot;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.Random;

import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application.
     */
    public static void main(String[] args) {
        run(SampleApp.class, args);
    }

    /**
     * Very simple {@code CommandLineRunner} to get a
     * random {@code Integer} value and throw exception if value
     * is higher than or equal to 5. This code is executed
     * when we run the application.
     *
     * @return Simple application logic.
     */
    @Bean
    CommandLineRunner randomException() {
        return args -> {
            final Integer value = new Random().nextInt(10);
            if (value >= 5) {
                throw new TooHighException(String.format("Value %d is too high", value));
            } else {
                System.out.println(value);
            }
        };
    }

    /**
     * Exception when a Integer value is too high.
     * We implement the {@code ExitCodeGenerator} interface and
     * annotate this class as a Spring component. Spring Boot
     * will look for classes that implement {@code ExitCodeGenerator}
     * and use them to get a exit code.
     */
    static class TooHighException extends Exception implements ExitCodeGenerator {
        TooHighException(final String message) {
            super(message);
        }

        /**
         * @return Always return 42 as exit code when this exception occurs.
         */
        @Override
        public int getExitCode() {
            return 42;
        }
    }

}

But there is also an exit method available in the SpringApplication class. This method determines the exit code value by looking up beans in the application context that implement the ExitCodeExceptionMapper interface. The ExitCodeExceptionMapper interface has the method getExitCode(Throwable). We can write an implementation that has logic to return different exit codes based on characteristics of the given Throwable. This is especially useful when we want to have an exit code for exceptions from third party libraries.

In the following example we use the exit method of SpringApplication and a Spring bean exitCodeExceptionMapper that implements the ExitCodeExceptionMapper interface using a Java 8 lambda expression:

// File: src/main/java/mrhaki/springboot/SampleApp.java
package mrhaki.springboot;

import org.jooq.DSLContext;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeExceptionMapper;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DataAccessResourceFailureException;

import static mrhaki.springboot.sql.Tables.USERS;
import static org.springframework.boot.SpringApplication.exit;
import static org.springframework.boot.SpringApplication.run;

@SpringBootApplication
public class SampleApp {

    /**
     * Run the application, let Spring Boot
     * decide the exit code and use the
     * exit code value for {@code System.exit()} method.
     */
    public static void main(String[] args) {
        System.exit(exit(run(SampleApp.class, args)));
    }

    /**
     * Very simple {@code CommandLineRunner} to get a
     * a list of users from a Postgresql database 
     * using JOOQ.
     *
     * @return Simple application logic.
     */
    @Bean
    CommandLineRunner showUsers(final DSLContext dslContext) {
        return args -> dslContext.select(USERS.NAME)
                                 .from(USERS)
                                 .fetch()
                                 .forEach(name -> System.out.println(name.get(USERS.NAME)));
    }

    /**
     * Spring bean that implements the {@code ExitCodeExceptionMapper}
     * interface using a lambda expression.
     */
    @Bean
    ExitCodeExceptionMapper exitCodeExceptionMapper() {
        return exception -> {
            // Using ExitCodeExceptionMapper we can set
            // the exit code ourselves, even if we didn't
            // write the exception ourselves.
            if (exception.getCause() instanceof DataAccessResourceFailureException) {
                return 42;
            }
            return 1;
        };
    }

}

Written with Spring Boot 1.5.2.RELEASE.

December 19, 2016

Spring Sweets: Add (Extra) Build Information To Info Endpoint

With Spring Boot Actuator we get some useful endpoints in our application to check on our application when it is running. One of the endpoints is the /info endpoint. We can add information about our application if Spring Boot finds a file META-INF/build-info.properties in the classpath of our application. With the Gradle Spring Boot plugin we can generate the build-info.properties file. When we apply the Gradle Spring Boot plugin to our project we get a Gradle extension springBoot in our build file. With this extension we can configure Spring Boot for our project. To generate project information that is used by the /info endpoint we must add the method statement buildInfo() inside the springBoot extension. With this method statement the Gradle Spring Boot plugin generates a file build/main/resources/META-INF/build-info.properties..

// File: build.gradle
plugins {
    id 'org.springframework.boot' version '1.4.2.RELEASE'
}
...
springBoot {
    // This statement tells the Gradle Spring Boot plugin
    // to generate a file 
    // build/resources/main/META-INF/build-info.properties
    // that is picked up by Spring Boot to display
    // via /info endpoint.
    buildInfo()
}
...

Let's run our application and send a request for /info:

$ http -b localhost:8080/info
{
    "build": {
        "artifact": "spring-boot-sample",
        "group": "mrhaki.spring",
        "name": "sample-mrhaki",
        "time": 1482139076000,
        "version": "0.3.0"
    }
}
$

To override the default properties or to add new properties we must provide a configuration closure to the buildInfo method. If we a built-in key as the name of the property it is overridden with a new value, otherwise the key is added as a new property. In the following example we add some extra properties and override the properties time and name:

// File: build.gradle
...
springBoot {
    buildInfo {
        // Generate extra build info.
        additionalProperties = [
                by: System.properties['user.name'],
                operatingSystem: "${System.properties['os.name']} (${System.properties['os.version']})",
                continuousIntegration: System.getenv('CI') ? true: false,
                machine: InetAddress.localHost.hostName,
                // Override buildInfo property time
                time: buildTime(),
                // Override name property
                name: 'sample-springboot-app'
        ]
    }
}

def buildTime() {
    final dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ")
    dateFormat.timeZone = TimeZone.getTimeZone('GMT')
    dateFormat.format(new Date())
}
...

We restart the application and invoke the /info endpoint to get more results for the build:

$ http -b localhost:8080/info
{
    "build": {
        "artifact": "spring-boot-sample",
        "by": "mrhaki",
        "continuousIntegration": "false",
        "group": "mrhaki.spring",
        "machine": "mrhaki-laptop-2015.local",
        "name": "sample-springboot-app",
        "operatingSystem": "Mac OS X (10.12.2)",
        "time": "2016-12-19 09:16:50+0000",
        "version": "0.3.0"
    }
}
$

Written with Spring Boot 1.4.2.RELEASE.

Spring Sweets: Add Git Info To Info Endpoint

With Spring Boot Actuator we get some endpoints that display information about our application. One of the endpoints is the /info endpoint. If our project uses Git we can add information about Git to the /info endpoint. By default Spring Boot will look for a file git.properties in the classpath of our application. The file is a Java properties file with keys that start with git. and have values like the branch name, commit identifier and commit message. Spring Boot uses this information and when we request the /info endpoint we get a response with the information. This can be very useful to check the Git information that was used to build the application. To create the git.properties file we can use a Gradle (or Maven) plugin that will do the work for us.

In the following example we use the Gradle plugin to generate the git.properties file for our project. The Gradle Git properties plugin is added in the plugins configuration block. The plugin adds a Gradle extension gitProperties that can be used to customize the output in git.properties. We could even change the location, but we keep it to the default location which is build/resources/main/git.properties.

// File: build.gradle
plugins {
    id 'org.springframework.boot' version '1.4.2.RELEASE'
    // Add Git properties plugin.
    id 'com.gorylenko.gradle-git-properties' version '1.4.17'
}

// Customize Git properties plugin.
gitProperties {
    // Change date format in git.properties file.
    dateFormat = "yyyy-MM-dd HH:mm:ssZ"
    dateFormatTimeZone = 'GMT'
}

That is all we need to do. The Git properties plugin generates the git.properties file that is need by Spring Boot. Let's run our application and send a request for the /info endpoint:

$ http -b localhost:8080/info
{
    "git": {
        "branch": "feature/dsl-hapi",
        "commit": {
            "id": "511d269",
            "time": "2016-12-15 12:28:20+0000"
        }
    }
}
$

We can even show more information by changing the Spring Boot configuration property management.git.info.mode. The default value is SIMPLE, but we can also use the value FULL. Then we get the Git commit message and user details. We can set the configuration property in different ways for our Spring Boot application. For example in application.yml, Java system property or environment variable. In our example we add a Java system property to the Gradle bootRun task:

// File: build.gradle
...
bootRun {
    systemProperty 'management.info.git.mode', 'FULL'
}
...

We execute the bootRun task and send a new request for the /info endpoint:

$ http -b localhost:8080/info
{
    "git": {
        "branch": "feature/dsl-hapi",
        "commit": {
            "id": "511d2693cbabc34449f838bf856fa2b9989cbca6",
            "id.abbrev": "511d269",
            "message": {
                "full": "Enable Codenarc checks for integration test code",
                "short": "Enable Codenarc checks for integration test code"
            },
            "time": "2016-12-15 12:28:20+0000",
            "user": {
                "email": "mrhaki@server",
                "name": "Hubert Klein Ikkink"
            }
        }
    }
}
$

This time we see more information about Git.

Written with Spring Boot 1.4.2.RELEASE

July 2, 2016

Spring Sweets: Using Groovy Configuration As PropertySource

We have many ways to provide configuration properties to a Spring (Boot) application. We can add our own custom configuration properties format. For example we can use Groovy's ConfigObject object to set configuration properties. We need to read a configuration file using ConfigSlurper and make it available as a property source for Spring. We need to implement two classes and add configuration file to support a Groovy configuration file in a Spring application.

First we need to write a class that extends the PropertySource in the package org.springframework.core.env. This class has methods to get property values based on a given key. There are already some subclasses for specific property sources. There is for example also a MapPropertySource class. We will extend that class for our implementation, because we can pass our flattened ConfigObject and rely on all existing functionality of the MapPropertySource class:

// File: src/main/java/mrhaki/spring/configobject/ConfigObjectPropertySource.java
package mrhaki.spring.configobject;

import groovy.util.ConfigObject;
import org.springframework.core.env.MapPropertySource;

/**
 * Property source that supports {@link ConfigObject}. The {@link ConfigObject}
 * is flattened and all functionality is delegated to the
 * {@link MapPropertySource}.
 */
public class ConfigObjectPropertySource extends MapPropertySource {
    
    public ConfigObjectPropertySource(
            final String name, 
            final ConfigObject config) {
        
        super(name, config.flatten());
    }
    
}

Next we must implement a class that loads the configuration file. The configuration file must be parsed and we must our ConfigObjectPropertySource so we can use it in our Spring context. We need to implement the PropertySourceLoader interface, where we can define the file extensions the loader must be applied for. Also we override the load method to load the actual file. In our example we also add some extra binding variables we can use when we define our Groovy configuration file.

// File: src/main/java/mrhaki/spring/configobject/ConfigObjectPropertySourceLoader.java
package mrhaki.spring.configobject;

import groovy.util.ConfigObject;
import groovy.util.ConfigSlurper;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ConfigObjectPropertySourceLoader implements PropertySourceLoader {

    /**
     * Allowed Groovy file extensions for configuration files.
     *
     * @return List of extensions for Groovy configuration files.
     */
    @Override
    public String[] getFileExtensions() {
        return new String[]{"groovy", "gvy", "gy", "gsh"};
    }

    /**
     * Load Groovy configuration file with {@link ConfigSlurper}.
     */
    @Override
    public PropertySource<?> load(
            final String name,
            final Resource resource,
            final String profile) throws IOException {

        if (isConfigSlurperAvailable()) {
            final ConfigSlurper configSlurper = profile != null ?
                    new ConfigSlurper(profile) :
                    new ConfigSlurper();

            // Add some extra information that is accessible
            // in the Groovy configuration file.
            configSlurper.setBinding(createBinding(profile));
            final ConfigObject config = configSlurper.parse(resource.getURL());

            // Return ConfigObjectPropertySource if configuration
            // has key/value pairs.
            return config.isEmpty() ?
                    null :
                    new ConfigObjectPropertySource(name, config);
        }

        return null;
    }

    private boolean isConfigSlurperAvailable() {
        return ClassUtils.isPresent("groovy.util.ConfigSlurper", null);
    }

    private Map<String, Object> createBinding(final String profile) {
        final Map<String, Object> bindings = new HashMap<>();
        bindings.put("userHome", System.getProperty("user.home"));
        bindings.put("appDir", System.getProperty("user.dir"));
        bindings.put("springProfile", profile);
        return bindings;
    }

}

Finally we need to add our ConfigObjectPropertySourceLoader to a Spring configuration file, so it used in our Spring application. We need to create the file spring.factories in the META-INF directory with the following contents:

# File: src/main/resources/META-INF/spring.factories
org.springframework.boot.env.PropertySourceLoader=\
  mrhaki.spring.configobject.ConfigObjectPropertySourceLoader

Now when we create a Spring application with all our classes and configuration file in the classpath we can provide an application.groovy file in the root of the classpath:

// File: src/main/resources/application.groovy
app {
    // Using binding property appDir,
    // which is added to the ConfigSlurper 
    // in ConfigObjectPropertySourceLoader.
    startDir = appDir

    // Configuration hierarchy.
    message {
        text = "Text from Groovy configuration!"
    }
}

environments {
    // When starting application with 
    // -Dspring.profiles.active=blog this
    // environment configuration is used.
    blog {
        app {
            message {
                text = "Groovy Goodness"
            }
        }
    }
}

Written with Spring Boot 1.3.5.RELEASE

June 16, 2016

Spring Sweets: Running Our Own Spring Initializr Server

To start a new project based on Spring or Spring Boot we can use the website start.spring.io. We can easily create a project templates based on Maven or Gradle and define all needed dependencies by clicking on checkboxes in the UI. In a previous post we also learned how to create a project using a URL using the same start.spring.io website. The start.spring.io website is actually a Spring Boot application and we can easily host our own server. With our own server we can for example limit the number of dependencies, force Gradle as the only build tool and set default values for project name, packages and much more.

To get started we must first clone the GitHub project. We need to build the project with Maven to install the libraries in our local Maven repository. After that is done we are reading to use it in our own Spring Boot application that is our customised Spring Initializr server. The easiest way to run the server is to have the Spring CLI tool installed. The easiest way to install it is using SDKMAN!. We type on the command line $ sdk install springboot.

Next we create a new directory and inside the directory we create a new Groovy file initializr.groovy:

package app

@Grab('io.spring.initalizr:initializr-web:1.0.0.BUILD-SNAPSHOT')
@Grab('spring-boot-starter-web')
class InitializerService {}

Next we need a configuration file which all the options for the Spring Initializr server. We can start by copying the file application.yml from the initializr-service project on GitHub to our directory with the file initializr.groovy. This file contains a lot of information. The configuration format is explained on the GitHub wiki, but it is really straight forward. If we open the file we can for example remove dependencies from the dependencies or set default values for the groupId. If we only want to support Gradle we can remove the Maven references from the types section. And of course to have Groovy as the default language we can in the languages section set the default to true for Groovy.

...
  artifactId:
    value: sample
  groupId:
    value: com.mrhaki
  version:
    value: 1.0.0.DEVELOPMENT
  name:
    value: Sample
  description:
    value: Sample Project
  packageName:
    value: com.mrhaki.demo
...
  types:
    - name: Gradle Project
      id: gradle-project
      description: Generate a Gradle based project archive
      sts-id: gradle.zip
      tags:
        build: gradle
        format: project
      default: false
      action: /starter.zip
    - name: Gradle Config
      id: gradle-build
      description: Generate a Gradle build file
      sts-id: build.gradle
      tags:
        build: gradle
        format: build
      default: true
      action: /build.gradle
...
  javaVersions:
    - id: 1.8
      default: true
  languages:
    - name: Java
      id: java
      default: false
    - name: Groovy
      id: groovy
      default: true
...

And that is it! We are ready to start our own Spring Initializr server:

$ spring run initializr.groovy
...

For a finishing touch we can also override the static resources and templates of the server application. For example we can add a new spring.css file in the directory static/css. And place a file home.html in the directory templates. We can take the existing files as sample and change what we want.

The following screenshot shows a customized template with some style changes:

We can even use our server to create a project from IntelliJ IDEA. When we create a new project and select Spring Initializr from the list box on the left we can type in the URL of our server:

Next we see our default values for the project details:

And finally we can select the dependencies we have defined in our application.yml file:

March 16, 2016

Spring Sweets: Create New Projects From a URL

To quickly start with a Spring project we can use the website start.spring.io. Via a user interface we can set project properties and at the end we have a project archive (Zip or gzipped Tar file) or build file (pom.xml or build.gradle). We can also directory access an URL to create the output files and we set the properties via request parameters. This way you can share a link with someone and if they click on it they will download the generated project archive or build files.

We can choose different base URLs depending on the type of project archive we want. To get a Zip file we use http://start.spring.io/starter.zip and to get a gzipped Tar file we use http://start.spring.io/starter.tgz. To create a Gradle build file with all the configuration for a project we use http://start.spring.io/build.gradle. For a Maven POM XML file we use the URL http://start.spring.io/pom.xml.

All output format support the same request parameters to set the project properties:

Request parameterDescriptionSample
dependenciesAdd Spring Boot Starters and dependencies to your applicationweb,security
styleAlias for dependenciesactuator,sleuth,eureka&20discovery
typeUsed for project archives. Use gradle-project or maven-projectgradle-project
nameName of projectdemo
descriptionDescription for the projectDemo%20project%20for%20Spring%20Boot
groupIdValue for groupId for publishing projectcom.example
artifactIdValue for artifactId for publishing projectdemo
versionVersion of the project1.0.0.DEVELOPMENT
bootVersionVersion for Spring Boot1.3.3
packagingPackaging for project (jar or war)jar
applicationNameName of the applicationdemo
languageLanguage can be Java, Groovy or KotlinGroovy
packageNameName for package for example codecom.example
javaVersionJava version for project1.8
baseDirBase directory in archivesample


Let's create a URL for a Groovy project with a Gradle build file where we have a dependency on web, security and actuator:

http://start.spring.io/build.gradle?dependencies=web,security,actuator&name=sample&description=Sample&20Project&version=1.0.0.DEVELOPMENT&bootVersion=1.3.3.RELEASE&javaVersion=1.8&language=groovy&packaging=jar

When we save the Gradle build file we have the following file contents:

buildscript {
 ext {
  springBootVersion = '1.3.3.RELEASE'
 }
 repositories {
  mavenCentral()
 }
 dependencies {
  classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
 }
}

apply plugin: 'groovy'
apply plugin: 'eclipse'
apply plugin: 'spring-boot' 

jar {
 baseName = 'demo'
 version = '1.0.0.DEVELOPMENT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
 mavenCentral()
}


dependencies {
 compile('org.springframework.boot:spring-boot-starter-actuator')
 compile('org.springframework.boot:spring-boot-starter-security')
 compile('org.springframework.boot:spring-boot-starter-web')
 compile('org.codehaus.groovy:groovy')
 testCompile('org.springframework.boot:spring-boot-starter-test') 
}


eclipse {
 classpath {
   containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
   containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
 }
}

task wrapper(type: Wrapper) {
 gradleVersion = '2.9'
}

Instead of just a build file we want to create a sample project archive file. We use the following URL:

http://start.spring.io/starter.tgz?dependencies=web,security,actuator&name=sample&description=Sample&20Project&version=1.0.0.DEVELOPMENT&bootVersion=1.3.3.RELEASE&javaVersion=1.8&language=groovy&packaging=jar&type=gradle-project&baseDir=sample

Let's open the archive and see the contents. Notice we used the baseDir request parameter so when we unpack we get a new directory.

$ tar xf starter.tgz 
$ tree sample/
sample/
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── groovy
    │   │   └── com
    │   │       └── example
    │   │           └── SampleApplication.groovy
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── groovy
            └── com
                └── example
                    └── SampleApplicationTests.groovy

14 directories, 8 files
$

We can also use wget, cUrl or Httpie to download a starter template from start.spring.io using request parameters.

Written with Spring Boot 1.3.3.

September 21, 2015

Spring Sweets: Java System Properties As Configuration Properties With Spring Boot

In a previous post we learned that configuration property values can be passed via environment variables. With Spring Boot we can also pass the values using Java system properties. So if we have a property sample.message then we can use -Dsample.message=value to pass a value when we run the application. If we use the Spring Boot Gradle plugin we must reconfigure the bootRun task to pass Java system properties from the command-line.

Let's reuse our sample application from the previous blog post:

package com.mrhaki.spring

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.stereotype.Controller

@SpringBootApplication
@Controller
class Application implements CommandLineRunner {

    @Value('${sample.message:"default"}')
    String message

    @Override
    void run(final String... args) throws Exception {
        println "Spring Boot says: $message"
    }

    static void main(String[] args) {
        SpringApplication.run(Application, args)
    }

}

Our Gradle build file now looks like this:

buildscript {
    repositories.jcenter()

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE'
    }
}

apply plugin: 'groovy'
apply plugin: 'spring-boot'

repositories.jcenter()

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
    compile 'org.springframework.boot:spring-boot-starter'
}

// Reconfigure bootRun task to pass
// along the Java system properties
// from the command-line.
bootRun {
    systemProperties System.properties
}

Now we can run the following command from the command-line and set a value for the sample.message configuration property:

$ gradle -Dsample.message="Set by Java sys prop." -q bootRun
...
Spring Boot says: Set by Java sys prop.
...
$ 

Written with Spring Boot 1.2.5.RELEASE and Gradle 2.7.

September 20, 2015

Spring Sweets: Setting Configuration Properties Via Environment Variables

Spring Boot has many options for externalising the configuration of our application. One of the options it to use OS environment variables. We need to follow certain rules to name the environment variable and then Spring Boot will use the value of variable to set a configuration property. The property name must be in uppercase and any dots need to be replaced with underscores. For example the configuration property sample.message is set with the environment variable SAMPLE_MESSAGE. This feature can be useful in a continuous integration environment where we can set environment variables or just when developing locally. The nice thing is that this also works when we use the Spring Boot Gradle plugin. The environment variables are passed on to the Java process that the bootRun task starts.

The following source file is a simple Spring Boot command-line application. The sample.message property can be configured as by Spring. If there is no value set the default value "default" is used.

package com.mrhaki.spring

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.stereotype.Controller

@SpringBootApplication
@Controller
class Application implements CommandLineRunner {

    @Value('${sample.message:"default"}')
    String message

    @Override
    void run(final String... args) throws Exception {
        println "Spring Boot says: $message"
    }

    static void main(String[] args) {
        SpringApplication.run(Application, args)
    }

}

We use the following Gradle build file with the Spring Boot Gradle plugin:

buildscript {
    repositories.jcenter()

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE'
    }
}

apply plugin: 'groovy'
apply plugin: 'spring-boot'

repositories.jcenter()

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
    compile 'org.springframework.boot:spring-boot-starter'
}

If we run the bootRun task without any environment variables set we get the default value:

$ gradle -q bootRun
...
Spring Boot says: "default"
...
$ 

Now we run the same task but set the environment variable SAMPLE_MESSAGE:

$ SAMPLE_MESSAGE="Message from env." gradle -q bootRun
...
Spring Boot says: Message from env.
...
$ 

Written with Spring Boot 1.2.5.RELEASE and Gradle 2.7.

September 17, 2015

Spring Sweets: Report Applied Auto-configuration Spring Boot

The auto-configuration feature in Spring Boot adds beans to our application context based on conditions. For example based on the availability of a class on the class path or a environment property beans are enabled or disabled. We must apply the @EnableAutoConfiguration or @SpringBootApplicaiton in our Spring Boot application to make this work. To get an overview of all the configurations that had positive and negative conditional matches in our application we can use the --debug command-line option. This will print out a report to System.out with a complete overview. We can check why a configuration is applied or not.

In the following Gradle build file we add the option --debug to the args property of the bootRun task:

buildscript {
    ext {
        springBootVersion = '1.2.5.RELEASE'
    }
    repositories {
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 
    }
}

apply plugin: 'groovy'
apply plugin: 'spring-boot' 

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    jcenter()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
    compile "org.springframework.boot:spring-boot-starter-actuator"
    compile "org.springframework.boot:spring-boot-starter-groovy-templates"
    compile "org.springframework.boot:spring-boot-starter-web"
    runtime "com.h2database:h2"
    testCompile "org.springframework.boot:spring-boot-starter-test"
}

bootRun {
    // Use --debug option to print out 
    // auto-configuration report.
    args '--debug'
}

Let's run the bootRun task and check the output (lines are omitted in the output shown next):

$ gradle bootRun
...
=========================
AUTO-CONFIGURATION REPORT
=========================


Positive matches:
-----------------

...
   GroovyTemplateAutoConfiguration
      - @ConditionalOnClass classes found: groovy.text.markup.MarkupTemplateEngine (OnClassCondition)

   GroovyTemplateAutoConfiguration.GroovyMarkupConfiguration
      - @ConditionalOnClass classes found: org.springframework.web.servlet.view.groovy.GroovyMarkupConfigurer (OnClassCondition)

   GroovyTemplateAutoConfiguration.GroovyMarkupConfiguration#groovyMarkupConfigurer
      - @ConditionalOnMissingBean (types: org.springframework.web.servlet.view.groovy.GroovyMarkupConfig; SearchStrategy: all) found no beans (OnBeanCondition)

   GroovyTemplateAutoConfiguration.GroovyWebConfiguration
      - @ConditionalOnClass classes found: javax.servlet.Servlet,org.springframework.context.i18n.LocaleContextHolder,org.springframework.web.servlet.view.UrlBasedViewResolver (OnClassCondition)
      - found web application StandardServletEnvironment (OnWebApplicationCondition)
      - matched (OnPropertyCondition)

   GroovyTemplateAutoConfiguration.GroovyWebConfiguration#groovyMarkupViewResolver
      - @ConditionalOnMissingBean (names: groovyMarkupViewResolver; SearchStrategy: all) found no beans (OnBeanCondition)
...

Negative matches:
-----------------

   ActiveMQAutoConfiguration
      - required @ConditionalOnClass classes not found: javax.jms.ConnectionFactory,org.apache.activemq.ActiveMQConnectionFactory (OnClassCondition)
...
$

Written with Spring Boot 1.2.5.RELEASE

September 16, 2015

Spring Sweets: Reload Classes Spring Boot With Spring Loaded And Gradle Continuous Build

When we develop a Spring Boot application we can hot reload newly compiled classes using Gradle. The bootRun task of the Spring Boot Gradle plugin has support for Spring Loaded. We must add a dependency to Spring Loaded as a dependency for the classpath configuration in the buildscript block. If we now use the bootRun task everything is ready to reload changed classes. Now we can use the continuous build feature of Gradle to listen for changes in our source files to recompile them. The recompiled classes are then reloaded in the running Spring Boot application started with bootRun. We start a second Gradle instance with the -t option for the classes task. This way when we change a source file it gets recompiled automatically and reloaded.

The following build script shows how we add Spring Loaded:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE"
        classpath 'org.springframework:springloaded:1.2.4.RELEASE'
    }
}

apply plugin: 'groovy'
apply plugin: 'spring-boot'

jar {
    baseName = 'sample'
    version =  '1.0'
}

repositories {
    jcenter()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile "org.springframework.boot:spring-boot-starter-web"
    compile 'org.codehaus.groovy:groovy-all:2.4.4'
}

Let's also create a very simple Spring Boot application:

package com.mrhaki.sample

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.ResponseBody

@EnableAutoConfiguration
@Controller
class SampleController {

    @RequestMapping(value = '/', method = RequestMethod.GET)
    @ResponseBody
    String sample() {
        'Sample controller'
    }

    static void main(String... args) {
        SpringApplication.run(SampleController, args)
    }

}

Now we can start a Gradle proces that will recompile class files if the source file changes:

$ gradle -t classes
Continuous build is an incubating feature.
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes

BUILD SUCCESSFUL

Total time: 2.847 secs

Waiting for changes to input files of tasks... (ctrl-d to exit)

In another terminal we start the bootRun task:

$ gradle bootRun
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:findMainClass
:bootRun
objc[16206]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/bin/java and /Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/libinstrument.dylib. One of the two will be used. Which one is undefined.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.5.RELEASE)

...
> Building 83% > :bootRun

Written with Gradle 2.7 and Spring Boot 1.2.5.

April 17, 2015

Spring Sweets: Using @Value for Constructor Arguments

In Spring we can use the @Value annotation to set property or arguments values based on a SpEL expression. If we want to use the @Value annotation for a constructor argument we must not forget to add the @Autowired annotation on the constructor as well.

// File: sample/Message.groovy
package sample

import org.springframework.beans.factory.annotation.*
import org.springframework.stereotype.*

@Component
class Message {

    final String text

    // Use @Autowired to get @Value to work.
    @Autowired
    Message(
        // Refer to configuration property
        // app.message.text to set value for 
        // constructor argument text.
        @Value('${app.message.text}') final String text) {
        this.text = text
    }

}

Written with Spring 4.1.6.