16

I am currently having trouble with my dataSource bean creation on condition of String property from my applications.yaml file.

Ideally, I would only like to create the dataSource bean only if the url is set in my application.yaml file. Shouldn't create the bean if its not present (empty or null). I know this condition checks on boolean but is there anyway to check if the string property is empty or null?

DatabaseConfig.java

@Configuration
public class DatabaseConfig {
    @Value("${database.url:}")
    private String databaseUrl;

    @Value("${database.username:}")
    private String databaseUsername;

    @Value("${database.password:}")
    private String databasePassword;

    protected static final String DRIVER_CLASS_NAME = "com.sybase.jdbc4.jdbc.SybDriver";


    /**
     * Configures and returns a datasource. Optional
     *
     * @return A datasource.
     */
    @ConditionalOnProperty(value = "database.url")
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
            .url(testDatabaseUrl)
            .username(testDatabaseUsername)
            .password(testDatabasePassword)
            .build();
    }
}

application.yml (This field will be optional)

database:
  url: http://localhost:9000
user3712237
  • 173
  • 1
  • 1
  • 8
  • `@ConditionalOnProperty` already has a `matchIfMissing` parameter to control what happens when the property is not present. Are you trying to say that the empty string value is a synonym for not-present in your code? – Andy Brown Sep 08 '17 at 15:22

4 Answers4

26

You could use @ConditionalOnExpression with a Spring Expression Language (SpEL) expression as value:

@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${database.url:}')")
@Bean
public DataSource dataSource() {
    // snip
}

For better readability and reusability you could turn this into an annotation:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${database.url:}')")
public @interface ConditionalOnDatabaseUrl {
}

@ConditionalOnDatabaseUrl
@Bean
public DataSource dataSource() {
    // snip
}
sfussenegger
  • 35,575
  • 15
  • 95
  • 119
  • May I know why we need generic type over there? T(org.springframework.util.StringUtils) – Hong Dec 31 '21 at 03:18
  • 2
    @Hong the `T(..)` is to reference types and has nothing to do with generics. I assume you were thinking of ``. Here's the SpEL reference: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-types – sfussenegger Jan 10 '22 at 16:35
17

I'm having the same issue. As I undestood, you'r looking for "property exists and not empty" condition. This condition doesn't come out of the box, therefore some coding required. Solution using org.springframework.context.annotation.Conditional works for me:

  1. Create ConditionalOnPropertyNotEmpty annotation and accompanied OnPropertyNotEmptyCondition class as follows:

     // ConditionalOnPropertyNotEmpty.java
    
     import org.springframework.context.annotation.Condition;
     import org.springframework.context.annotation.ConditionContext;
     import org.springframework.context.annotation.Conditional;
     import org.springframework.core.type.AnnotatedTypeMetadata;
     import java.lang.annotation.ElementType;
     import java.lang.annotation.Retention;
     import java.lang.annotation.RetentionPolicy;
     import java.lang.annotation.Target;
     import java.util.Map;
    
     @Target({ ElementType.TYPE, ElementType.METHOD })
     @Retention(RetentionPolicy.RUNTIME)
     @Conditional(ConditionalOnPropertyNotEmpty.OnPropertyNotEmptyCondition.class)
     public @interface ConditionalOnPropertyNotEmpty {
         String value();
    
         class OnPropertyNotEmptyCondition implements Condition {
    
             @Override
             public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
                 Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyNotEmpty.class.getName());
                 if (attrs == null) {
                     return false;
                 }
                 String propertyName = (String) attrs.get("value");
                 String val = context.getEnvironment().getProperty(propertyName);
                 return val != null && !val.trim().isEmpty();
             }
         }
     }
    
  2. Annotate your bean with ConditionalOnPropertyNotEmpty, like:

     @ConditionalOnPropertyNotEmpty("database.url")
     @Bean
     public DataSource dataSource() { ... }
    

--

Alternatively, you can either set detabase.url to false explicitly (e.g. detabase.url: false) or do not define detabase.url at all in your config. Then, @ConditionalOnProperty(value = "database.url") will work for you as expected.

marcelosalloum
  • 3,481
  • 4
  • 39
  • 63
andrii
  • 1,248
  • 1
  • 11
  • 25
1

You can try more generic purpose annotation which is standard for the Spring Core: org.springframework.context.annotation.Conditional.

In the context objects for your callback you can get all the information you need: org.springframework.context.annotation.Condition

Also you can consider using Profiles.

alex.b
  • 1,448
  • 19
  • 23
0

The main answer above is correct, but since the isEmpty is deprecated, if you (likely) have apache commons loaded, you can use the isNotEmpty and simplify the condition

@ConditionalOnExpression(
    "T(org.apache.commons.lang3.StringUtils).isNotEmpty('${database.url:}')"
)
E Ciotti
  • 4,740
  • 1
  • 25
  • 17