After upgrading to Spring Boot 2.5.0, my application will no longer start locally when my local database user has no password.

I am able to get it to start if I set a password for the user and update my config accordingly.

Given the following properties:

spring:
  datasource:
    username: ${user.name}
    password: 
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost/database?OpenSourceSubProtocolOverride=true
    tomcat:
      initial-size: 2
      max-active: 25
      min-idle: 2
      max-idle: 2
      test-while-idle: true
      time-between-eviction-runs-millis: 30000

A NullPointerException is raised:

Caused by: java.lang.NullPointerException: null
    at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:na]
    at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006) ~[na:na]
    at java.base/java.util.Properties.put(Properties.java:1340) ~[na:na]
    at java.base/java.util.Properties.setProperty(Properties.java:228) ~[na:na]
    at org.apache.tomcat.jdbc.pool.DataSourceProxy.setPassword(DataSourceProxy.java:367) ~[tomcat-jdbc-9.0.46.jar:na]
    at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperty.set(DataSourceBuilder.java:431) ~[spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.jdbc.DataSourceBuilder$MappedDataSourceProperties.set(DataSourceBuilder.java:333) ~[spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.jdbc.DataSourceBuilder.build(DataSourceBuilder.java:179) ~[spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.createDataSource(DataSourceConfiguration.java:48) ~[spring-boot-autoconfigure-2.5.0.jar:2.5.0]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat.dataSource(DataSourceConfiguration.java:64) ~[spring-boot-autoconfigure-2.5.0.jar:2.5.0]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 163 common frames omitted

This results in:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.flywaydb.core.Flyway]: Factory method 'flyway' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 141 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectProvider.getIfUnique(DefaultListableBeanFactory.java:2063) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration$FlywayConfiguration.flyway(FlywayAutoConfiguration.java:116) ~[spring-boot-autoconfigure-2.5.0.jar:2.5.0]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 142 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 160 common frames omitted

After poking around a bit, I noticed this method in DataSourceProperties:

    public String determinePassword() {
        if (StringUtils.hasText(this.password)) {
            return this.password;
        }
        if (EmbeddedDatabaseConnection.isEmbedded(determineDriverClassName(), determineUrl())) {
            return "";
        }
        return null;
    }

Comment From: mbhave

@chrischall I wasn't able to reproduce the issue with a quick test on my end. Can you provide a small sample application that we can run to reproduce the issue?

Comment From: chrischall

Apologies. I neglected to include the type: org.apache.tomcat.jdbc.pool.DataSource in the yaml. I've attached demo.tar.gz which was built from start.spring.io and has been modified to include and use tomcat-jdbc for connection pooling. Here is its application.yaml for convenience:

spring:
  datasource:
    username: ${user.name}
    password: 
    driver-class-name: org.postgresql.Driver
    type: org.apache.tomcat.jdbc.pool.DataSource
    url: jdbc:postgresql://localhost/database

Comment From: snicoll

Thanks for the followup @chrischall. This is a regression in DataSourceBuilder.

Previously, we used the binder to bind a map of properties, where password was registered with null, and it does not invoke the setter if the value is null.

In 2.5.x, we use a DataSourceProperty that calls the method via reflection with a different mechanism. This one invokes the setter if the value is null.

Comment From: young0264

Apologies. I neglected to include the type: org.apache.tomcat.jdbc.pool.DataSource in the yaml. I've attached demo.tar.gz which was built from start.spring.io and has been modified to include and use tomcat-jdbc for connection pooling. Here is its application.yaml for convenience:

yaml spring: datasource: username: ${user.name} password: driver-class-name: org.postgresql.Driver type: org.apache.tomcat.jdbc.pool.DataSource url: jdbc:postgresql://localhost/database

THx Thx Thx Thx!