Chuck C
Chuck C

Reputation: 493

What is the best way to use @ConfigurationProperties with Builders?

I have searched and can't find any examples that would show me a better way to do this, but in the Spring/Spring Boot code, there are generic builders but the builder itself seems to apply the properties programmatically. Here is some code trying to configure 2 Oracle Connection Pool Data Sources:

import oracle.ucp.jdbc.PoolDataSourceFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.sql.SQLException;


@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(PoolDataSourceFactory.class)
public class PersistenceAutoConfiguration {

    @Bean (name = "readWriteDataSource")
    public DataSource getReadWriteDataSource() throws SQLException {
        OracleUcpDataSourceProperties rwProperties = getReadWriteProperties();

        return OracleUcpDataSourceBuilder.create()
                .connectionFactoryClassName(rwProperties.getConnectionFactoryClassName())
                .url(rwProperties.getUrl())
                .user(rwProperties.getUser())
                .password(rwProperties.getPassword())
                .initialPoolSize(rwProperties.getInitialPoolSize())
                .minPoolSize(rwProperties.getMinPoolSize())
                .maxPoolSize(rwProperties.getMaxPoolSize())
                .connectionWaitTimeout(rwProperties.getConnectionWaitTimeout())
                .inactiveConnectionTimeout(rwProperties.getInactiveConnectionTimeout())
                .maxIdleTime(rwProperties.getMaxIdleTime())
                .build();
    }

    @Bean (name = "readOnlyDataSource")
    public DataSource getReadOnlyDataSource() throws SQLException {
        OracleUcpDataSourceProperties roProperties = getReadOnlyProperties();

        return OracleUcpDataSourceBuilder.create()
                .connectionFactoryClassName(roProperties.getConnectionFactoryClassName())
                .url(roProperties.getUrl())
                .user(roProperties.getUser())
                .password(roProperties.getPassword())
                .initialPoolSize(roProperties.getInitialPoolSize())
                .minPoolSize(roProperties.getMinPoolSize())
                .maxPoolSize(roProperties.getMaxPoolSize())
                .connectionWaitTimeout(roProperties.getConnectionWaitTimeout())
                .inactiveConnectionTimeout(roProperties.getInactiveConnectionTimeout())
                .maxIdleTime(roProperties.getMaxIdleTime())
                .build();
    }

    @ConfigurationProperties(prefix = "datasource.readwrite")
    @Bean(name = "readWriteProperties")
    protected OracleUcpDataSourceProperties getReadWriteProperties() {
        return new OracleUcpDataSourceProperties();
    }

    @ConfigurationProperties(prefix = "datasource.readonly")
    @Bean(name = "readOnlyProperties")
    protected OracleUcpDataSourceProperties getReadOnlyProperties() {
        return new OracleUcpDataSourceProperties();
    }
}

and

public class OracleUcpDataSourceProperties {
    private String connectionFactoryClassName;
    private String url;
    private String user;
    private String password;
    private int initialPoolSize;
    private int minPoolSize;
    private int maxPoolSize;
    private int connectionWaitTimeout;
    private int inactiveConnectionTimeout;
    private int maxIdleTime;
    private Boolean validateConnectionOnBorrow;


    public String getConnectionFactoryClassName() {
        return connectionFactoryClassName;
    }

    public void setConnectionFactoryClassName(String connectionFactoryClassName) {
        this.connectionFactoryClassName = connectionFactoryClassName;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getInitialPoolSize() {
        return initialPoolSize;
    }

    public void setInitialPoolSize(int initialPoolSize) {
        this.initialPoolSize = initialPoolSize;
    }

    public int getMinPoolSize() {
        return minPoolSize;
    }

    public void setMinPoolSize(int minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getConnectionWaitTimeout() {
        return connectionWaitTimeout;
    }

    public void setConnectionWaitTimeout(int connectionWaitTimeout) {
        this.connectionWaitTimeout = connectionWaitTimeout;
    }

    public int getInactiveConnectionTimeout() {
        return inactiveConnectionTimeout;
    }

    public void setInactiveConnectionTimeout(int inactiveConnectionTimeout) {
        this.inactiveConnectionTimeout = inactiveConnectionTimeout;
    }

    public int getMaxIdleTime() {
        return maxIdleTime;
    }

    public void setMaxIdleTime(int maxIdleTime) {
        this.maxIdleTime = maxIdleTime;
    }

    public Boolean getValidateConnectionOnBorrow() {
        return validateConnectionOnBorrow;
    }

    public void setValidateConnectionOnBorrow(Boolean validateConnectionOnBorrow) {
        this.validateConnectionOnBorrow = validateConnectionOnBorrow;
    }
}

and

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

import java.sql.SQLException;

public class OracleUcpDataSourceBuilder {
    private PoolDataSource pds;

    /**
     * This will grab the pool factory and initialize it.
     */
    public OracleUcpDataSourceBuilder() throws SQLException {
        pds = PoolDataSourceFactory.getPoolDataSource();
    }

    public static OracleUcpDataSourceBuilder create() throws SQLException {
        return new OracleUcpDataSourceBuilder();
    }

    public OracleUcpDataSourceBuilder connectionFactoryClassName(String connectionFactoryClassName) throws SQLException {
        pds.setConnectionFactoryClassName(connectionFactoryClassName);
        return this;
    }

    public OracleUcpDataSourceBuilder url(String url) throws SQLException {
        pds.setURL(url);
        return this;
    }

    public OracleUcpDataSourceBuilder user(String user) throws SQLException {
        pds.setUser(user);
        return this;
    }

    public OracleUcpDataSourceBuilder password(String password) throws SQLException {
        pds.setPassword(password);
        return this;
    }

    public OracleUcpDataSourceBuilder initialPoolSize(int initialPoolSize) throws SQLException {
        pds.setInitialPoolSize(initialPoolSize);
        return this;
    }

    public OracleUcpDataSourceBuilder minPoolSize(int minPoolSize) throws SQLException {
        pds.setMinPoolSize(minPoolSize);
        return this;
    }

    public OracleUcpDataSourceBuilder maxPoolSize(int maxPoolSize) throws SQLException {
        pds.setMaxPoolSize(maxPoolSize);
        return this;
    }

    public OracleUcpDataSourceBuilder connectionWaitTimeout(int connectionWaitTimeout) throws SQLException {
        pds.setConnectionWaitTimeout(connectionWaitTimeout);
        return this;
    }

    public OracleUcpDataSourceBuilder inactiveConnectionTimeout(int inactiveConnectionTime) throws SQLException {
        pds.setInactiveConnectionTimeout(inactiveConnectionTime);
        return this;
    }

    public OracleUcpDataSourceBuilder maxIdleTime(int maxIdleTime) throws SQLException {
        pds.setMaxIdleTime(maxIdleTime);
        return this;
    }

    public PoolDataSource build() {
        return pds;
    }
}

Preferably, I would like to be able to apply the properties directly to the builder in one place. is this possible? what changes would I have to make?

Thanks...

Upvotes: 2

Views: 1869

Answers (1)

Gokhan Oner
Gokhan Oner

Reputation: 3257

Here is your builder, sir

public class OracleUcpDataSourceBuilder {

private Map<String, String> properties = new HashMap<String, String>();
private static final String[] REQ_PROPERTIES = new String[] {"username", "password", "URL"};

public static OracleUcpDataSourceBuilder create() {
    return new OracleUcpDataSourceBuilder();
}

public DataSource build() {
    for (String prop : REQ_PROPERTIES) {
        Assert.notNull(properties.get(prop), "Property is required:" + prop);   
    }
    PoolDataSource result = PoolDataSourceFactory.getPoolDataSource();
    bind(result);
    return result;
}

private void bind(DataSource result) {
    MutablePropertyValues properties = new MutablePropertyValues(this.properties);
    new RelaxedDataBinder(result).bind(properties);
}

public OracleUcpDataSourceBuilder URL(String url) {
    this.properties.put("URL", url);
    return this;
}

public OracleUcpDataSourceBuilder username(String username) {
    this.properties.put("username", username);
    return this;
}

public OracleUcpDataSourceBuilder password(String password) {
    this.properties.put("password", password);
    return this;
}

}

Just define a bean like this:

@Bean (name = "readOnlyDataSource")
@ConfigurationProperties(prefix = "datasource.readonly")
public DataSource getReadOnlyDataSource() {
    return OracleUcpDataSourceBuilder.create().build();
}

Just make sure that the property names are correct. Spring will take care of the rest.

Note: I use DataSourceBuilder or Spring as a reference.. You can check it's source code also.

Edit: Added some methods to make sure some properties are configured. But this way, you need to set those properties manually to make sure that they're available.

Upvotes: 2

Related Questions