
org.nuiton.topia.persistence.TopiaConfigurationBuilder Maven / Gradle / Ivy
package org.nuiton.topia.persistence;
/*
* #%L
* ToPIA Extension :: API
* %%
* Copyright (C) 2018 - 2022 Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.cfg.AvailableSettings;
import org.nuiton.topia.persistence.jdbc.JdbcConfiguration;
import org.nuiton.topia.persistence.jdbc.JdbcConfigurationBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.sql.Driver;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
/**
* Builder to create a {@link org.nuiton.topia.persistence.TopiaConfiguration} instance.
*
* A few methods from {@link org.nuiton.topia.persistence.jdbc.JdbcConfiguration} are delegated here.
*/
public class TopiaConfigurationBuilder {
protected JdbcConfigurationBuilder jdbcConfigurationBuilder = new JdbcConfigurationBuilder();
protected static final String TOPIA_SERVICE_CONFIGURATION_PREFIX = "topia.service.";
protected static final String CONFIG_DEFAULT_SCHEMA = AvailableSettings.DEFAULT_SCHEMA;
protected static final String CONFIG_USER = AvailableSettings.USER;
protected static final String CONFIG_PASS = AvailableSettings.PASS;
protected static final String CONFIG_DRIVER = AvailableSettings.DRIVER;
protected static final String CONFIG_URL = AvailableSettings.URL;
protected static final String CONFIG_PERSISTENCE_TOPIA_ID_FACTORY_CLASS_NAME =
"topia.persistence.topiaIdFactoryClassName";
protected static final String CONFIG_PERSISTENCE_INIT_SCHEMA =
"topia.persistence.initSchema";
protected static final String CONFIG_PERSISTENCE_VALIDATE_SCHEMA =
"topia.persistence.validateSchema";
protected static final String CONFIG_PERSISTENCE_USE_HIKARI_FOR_JDBC_CONNECTION_POOLING =
"topia.persistence.useHikariForJdbcConnectionPooling";
/**
* Configuration that must not be in {@link TopiaConfiguration#getHibernateExtraConfiguration()}.
*/
protected static final ImmutableSet MAIN_CONFIGURATION =
ImmutableSet.of(CONFIG_DRIVER, CONFIG_URL, CONFIG_USER, CONFIG_PASS);
/**
* Do nothing but throws {@link TopiaMisconfigurationException} if there is a problem in
* given configuration.
*
* @param topiaConfiguration FIXME
* @throws TopiaMisconfigurationException FIXME
*/
public void check(TopiaConfiguration topiaConfiguration) throws TopiaMisconfigurationException {
if (StringUtils.isBlank(topiaConfiguration.getJdbcConnectionUrl())) {
throw new TopiaMisconfigurationException("you must provide JDBC connection URL", topiaConfiguration);
}
if (StringUtils.isBlank(topiaConfiguration.getJdbcConnectionUser())) {
throw new TopiaMisconfigurationException("you must provide JDBC connection user", topiaConfiguration);
}
if (topiaConfiguration.getJdbcConnectionPassword() == null) {
throw new TopiaMisconfigurationException("you must provide JDBC connection password", topiaConfiguration);
}
if (topiaConfiguration.getJdbcDriverClass() == null) {
throw new TopiaMisconfigurationException("you must provide JDBC connection driver", topiaConfiguration);
}
Map hibernateExtraConfiguration = topiaConfiguration.getHibernateExtraConfiguration();
if (hibernateExtraConfiguration.containsKey(AvailableSettings.HBM2DDL_AUTO)) {
throw new TopiaMisconfigurationException("you must not use " + AvailableSettings.HBM2DDL_AUTO
+ " configuration directive, if you want Hibernate to update the schema, use " +
HibernateTopiaMigrationService.class.getName(), topiaConfiguration);
}
for (Map.Entry entry : hibernateExtraConfiguration.entrySet()) {
String key = entry.getKey();
boolean entryIsOk = (key.startsWith("hibernate.") || key.startsWith("javax.persistence."))
&& !MAIN_CONFIGURATION.contains(key);
if (!entryIsOk) {
throw new TopiaMisconfigurationException("hibernate extra configuration is " +
hibernateExtraConfiguration +
" but it should not contains a key " + key, topiaConfiguration);
}
}
}
public BeanTopiaConfiguration readProperties(File propertiesFile) {
Properties properties = new Properties();
try (FileInputStream fileInputStream = new FileInputStream(propertiesFile)) {
properties.load(fileInputStream);
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("topia configuration file cannot be found " + propertiesFile, e);
} catch (IOException e) {
throw new UncheckedIOException("unable to read " + propertiesFile, e);
}
return readProperties(properties);
}
public BeanTopiaConfiguration readProperties(Properties properties) {
return readMap((Map) ImmutableMap.copyOf(properties));
}
public BeanTopiaConfiguration readMap(Map configuration) {
// JDBC connection
String url = configuration.get(CONFIG_URL);
String user = configuration.get(CONFIG_USER);
String password = configuration.get(CONFIG_PASS);
String driverName = configuration.get(CONFIG_DRIVER);
JdbcConfiguration jdbcConfiguration;
if (StringUtils.isBlank(driverName)) {
jdbcConfiguration = jdbcConfigurationBuilder.forDatabase(url, user, password);
} else {
jdbcConfiguration = jdbcConfigurationBuilder.forDatabase(url, user, password, driverName);
}
BeanTopiaConfiguration result = new BeanTopiaConfiguration(jdbcConfiguration);
// Schema initialization
String initSchemaConfigValue = configuration.get(CONFIG_PERSISTENCE_INIT_SCHEMA);
boolean initSchema = StringUtils.isBlank(initSchemaConfigValue) || Boolean.parseBoolean(initSchemaConfigValue);
result.setInitSchema(initSchema);
// Schema validation
String validateSchemaConfigValue = configuration.get(CONFIG_PERSISTENCE_VALIDATE_SCHEMA);
boolean validateSchema = StringUtils.isBlank(validateSchemaConfigValue) || Boolean.parseBoolean(validateSchemaConfigValue);
result.setValidateSchema(validateSchema);
// Using Hikari CP
String useHikariForJdbcConnectionPoolingConfigValue = configuration.get(CONFIG_PERSISTENCE_USE_HIKARI_FOR_JDBC_CONNECTION_POOLING);
boolean useHikariForJdbcConnectionPooling = StringUtils.isBlank(useHikariForJdbcConnectionPoolingConfigValue) || Boolean.parseBoolean(useHikariForJdbcConnectionPoolingConfigValue);
result.setUseHikariForJdbcConnectionPooling(useHikariForJdbcConnectionPooling);
// others
String topiaIdFactoryClassName = configuration.get(CONFIG_PERSISTENCE_TOPIA_ID_FACTORY_CLASS_NAME);
if (!Strings.isNullOrEmpty(topiaIdFactoryClassName)) {
result.setTopiaIdFactoryClassName(topiaIdFactoryClassName);
}
result.setSchemaName(configuration.get(CONFIG_DEFAULT_SCHEMA));
// Hibernate
for (Map.Entry entry : configuration.entrySet()) {
String key = entry.getKey();
boolean keyMustBeIncludedInHibernateExtraConfiguration =
!MAIN_CONFIGURATION.contains(key)
&& (key.startsWith("hibernate.") || key.startsWith("javax.persistence."));
if (keyMustBeIncludedInHibernateExtraConfiguration) {
result.getHibernateExtraConfiguration().put(key, entry.getValue());
}
}
// services
for (Map.Entry entry : configuration.entrySet()) {
String prefixedConfigurationKey = entry.getKey();
String configurationValue = entry.getValue();
if (prefixedConfigurationKey.startsWith(TOPIA_SERVICE_CONFIGURATION_PREFIX)) {
String configurationKey = StringUtils.removeStart(prefixedConfigurationKey, TOPIA_SERVICE_CONFIGURATION_PREFIX);
String[] split = StringUtils.split(configurationKey, '.');
Preconditions.checkState(split.length > 0, "'" + prefixedConfigurationKey + "' is not a valid configuration key");
String serviceName = split[0];
if (split.length == 1) {
// service declaration instantiate it
try {
Class> loadedClass = Class.forName(configurationValue);
if (TopiaService.class.isAssignableFrom(loadedClass)) {
@SuppressWarnings("unchecked") Class extends TopiaService> serviceClass = (Class extends TopiaService>) loadedClass;
result.getDeclaredServices().put(serviceName, serviceClass);
} else {
throw new TopiaMisconfigurationException("class " + configurationValue + " is not a topia service, it does not implement " + TopiaService.class.getName(), null);
}
} catch (ClassNotFoundException e) {
throw new TopiaMisconfigurationException("unable to find class " + configurationValue + " in classpath", null);
}
} else {
// service configuration, save it to push it back later
Map serviceConfiguration = result.getDeclaredServicesConfiguration().computeIfAbsent(serviceName, aServiceName -> new LinkedHashMap<>());
String serviceConfigurationKey = StringUtils.removeStart(configurationKey, serviceName + ".");
serviceConfiguration.put(serviceConfigurationKey, configurationValue);
}
}
}
check(result);
return result;
}
public Map toMap(TopiaConfiguration topiaConfiguration) {
check(topiaConfiguration);
Map map = new LinkedHashMap<>();
// JDBC connection
map.put(CONFIG_URL, topiaConfiguration.getJdbcConnectionUrl());
map.put(CONFIG_USER, topiaConfiguration.getJdbcConnectionUser());
map.put(CONFIG_PASS, topiaConfiguration.getJdbcConnectionPassword());
map.put(CONFIG_DRIVER, topiaConfiguration.getJdbcDriverClass().getName());
// Schema initialization
if (!topiaConfiguration.isInitSchema()) {
map.put(CONFIG_PERSISTENCE_INIT_SCHEMA, String.valueOf(topiaConfiguration.isInitSchema()));
}
// Schema validation
if (!topiaConfiguration.isValidateSchema()) {
map.put(CONFIG_PERSISTENCE_VALIDATE_SCHEMA, String.valueOf(topiaConfiguration.isValidateSchema()));
}
// Using Hikari CP
if (!topiaConfiguration.isUseHikariForJdbcConnectionPooling()) {
map.put(CONFIG_PERSISTENCE_USE_HIKARI_FOR_JDBC_CONNECTION_POOLING, String.valueOf(topiaConfiguration.isUseHikariForJdbcConnectionPooling()));
}
// others
map.put(CONFIG_PERSISTENCE_TOPIA_ID_FACTORY_CLASS_NAME, topiaConfiguration.getTopiaIdFactory().getClass().getName());
if (topiaConfiguration.getSchemaName() != null) {
map.put(CONFIG_DEFAULT_SCHEMA, topiaConfiguration.getSchemaName());
}
// Hibernate
map.putAll(topiaConfiguration.getHibernateExtraConfiguration());
// services
for (Map.Entry> entry : topiaConfiguration.getDeclaredServices().entrySet()) {
String serviceName = entry.getKey();
String serviceDeclaration = TOPIA_SERVICE_CONFIGURATION_PREFIX + "." + serviceName;
String serviceClassName = entry.getValue().getName();
Map serviceConfiguration = topiaConfiguration.getDeclaredServicesConfiguration().get(serviceName);
map.put(serviceDeclaration, serviceClassName);
if (serviceConfiguration != null) {
for (Map.Entry serviceConfigurationEntry : serviceConfiguration.entrySet()) {
map.put(serviceDeclaration + "." + serviceConfigurationEntry.getKey(), serviceConfigurationEntry.getValue());
}
}
}
return map;
}
public Properties toProperties(TopiaConfiguration topiaConfiguration) {
Map map = toMap(topiaConfiguration);
Properties properties = new Properties();
properties.putAll(map);
return properties;
}
public ConfigureInitSchemaStep forDatabase(JdbcConfiguration jdbcConfiguration) {
BeanTopiaConfiguration beanTopiaConfiguration = new BeanTopiaConfiguration(jdbcConfiguration);
return new ConfigureInitSchemaStep(beanTopiaConfiguration);
}
public BeanTopiaConfiguration forTest(Class> testClass, String methodName) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forTestDatabase(testClass, methodName);
return forDatabase(jdbcConfiguration)
.onlyCreateSchemaIfDatabaseIsEmpty()
.validateSchemaOnStartup()
.useDefaultConnectionPool()
.build();
}
public class ConfigureInitSchemaStep {
protected BeanTopiaConfiguration beanTopiaConfiguration;
public ConfigureInitSchemaStep(BeanTopiaConfiguration beanTopiaConfiguration) {
this.beanTopiaConfiguration = beanTopiaConfiguration;
}
public ConfigureValidateSchemaStep onlyCreateSchemaIfDatabaseIsEmpty() {
beanTopiaConfiguration.setInitSchema(true);
return nextStep();
}
public ConfigureValidateSchemaStep useAlreadyExistingDatabaseAsIs() {
beanTopiaConfiguration.setInitSchema(false);
return nextStep();
}
public ConfigureValidateSchemaStep useHibernateUpdate() {
beanTopiaConfiguration.setInitSchema(true);
beanTopiaConfiguration.addDeclaredService("migration", org.nuiton.topia.persistence.HibernateTopiaMigrationService.class, new LinkedHashMap<>());
return nextStep();
}
public ConfigureValidateSchemaStep useFlyway() {
beanTopiaConfiguration.setInitSchema(true);
beanTopiaConfiguration.addDeclaredService("migration", "org.nuiton.topia.flyway.TopiaFlywayServiceImpl", new LinkedHashMap<>());
return nextStep();
}
public ConfigureValidateSchemaStep useLiquibase() {
beanTopiaConfiguration.setInitSchema(true);
beanTopiaConfiguration.addDeclaredService("migration", "org.nuiton.topia.flyway.TopiaLiquibaseServiceImpl", new LinkedHashMap<>());
return nextStep();
}
protected ConfigureValidateSchemaStep nextStep() {
return new ConfigureValidateSchemaStep(beanTopiaConfiguration);
}
}
public class ConfigureValidateSchemaStep {
protected BeanTopiaConfiguration beanTopiaConfiguration;
public ConfigureValidateSchemaStep(BeanTopiaConfiguration beanTopiaConfiguration) {
this.beanTopiaConfiguration = beanTopiaConfiguration;
}
public ConfigureJdbcConnectionPoolingStep validateSchemaOnStartup() {
beanTopiaConfiguration.setValidateSchema(true);
return new ConfigureJdbcConnectionPoolingStep(beanTopiaConfiguration);
}
public ConfigureJdbcConnectionPoolingStep doNotValidateSchemaOnStartup() {
beanTopiaConfiguration.setValidateSchema(false);
return new ConfigureJdbcConnectionPoolingStep(beanTopiaConfiguration);
}
}
public class ConfigureJdbcConnectionPoolingStep {
protected BeanTopiaConfiguration beanTopiaConfiguration;
public ConfigureJdbcConnectionPoolingStep(BeanTopiaConfiguration beanTopiaConfiguration) {
this.beanTopiaConfiguration = beanTopiaConfiguration;
}
public BuildStep useDefaultConnectionPool() {
return useHikariConnectionPool();
}
public BuildStep useHikariConnectionPool() {
beanTopiaConfiguration.setUseHikariForJdbcConnectionPooling(true);
return new BuildStep(beanTopiaConfiguration);
}
public BuildStep useC3p0ConnectionPool() {
beanTopiaConfiguration.setUseHikariForJdbcConnectionPooling(false);
return new BuildStep(beanTopiaConfiguration);
}
}
public class BuildStep {
protected BeanTopiaConfiguration beanTopiaConfiguration;
public BuildStep(BeanTopiaConfiguration beanTopiaConfiguration) {
this.beanTopiaConfiguration = beanTopiaConfiguration;
}
public BeanTopiaConfiguration build() {
check(beanTopiaConfiguration);
return beanTopiaConfiguration;
}
public Properties buildProperties() {
return toProperties(beanTopiaConfiguration);
}
public Map buildMap() {
return toMap(beanTopiaConfiguration);
}
}
/*
* A few methods that delegates to jdbcConfigurationBuilder
*/
public ConfigureInitSchemaStep forInMemoryH2Database() {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forInMemoryH2Database();
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forDatabase(String jdbcConnectionUrl, String jdbcConnectionUser, String jdbcConnectionPassword, String jdbcDriverClassName) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forDatabase(jdbcConnectionUrl, jdbcConnectionUser, jdbcConnectionPassword, jdbcDriverClassName);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forDatabase(String jdbcConnectionUrl, String jdbcConnectionUser, String jdbcConnectionPassword, Class extends Driver> jdbcDriverClass) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forDatabase(jdbcConnectionUrl, jdbcConnectionUser, jdbcConnectionPassword, jdbcDriverClass);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forDatabase(String jdbcConnectionUrl, String jdbcConnectionUser, String jdbcConnectionPassword) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forDatabase(jdbcConnectionUrl, jdbcConnectionUser, jdbcConnectionPassword);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forPostgresqlDatabase(String jdbcConnectionUrl, String jdbcConnectionUser, String jdbcConnectionPassword) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forPostgresqlDatabase(jdbcConnectionUrl, jdbcConnectionUser, jdbcConnectionPassword);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forH2Database(String jdbcConnectionUrl, String jdbcConnectionUser, String jdbcConnectionPassword) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forH2Database(jdbcConnectionUrl, jdbcConnectionUser, jdbcConnectionPassword);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forH2Database(String jdbcConnectionUrl) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forH2Database(jdbcConnectionUrl);
return forDatabase(jdbcConfiguration);
}
public ConfigureInitSchemaStep forH2Database(File file) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forH2Database(file);
return forDatabase(jdbcConfiguration);
}
// public ConfigureInitSchemaStep forH2DatabaseInTempDirectory() {
// JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forH2DatabaseInTempDirectory();
// return forDatabase(jdbcConfiguration);
// }
public ConfigureInitSchemaStep forTestDatabase(Class> testClass, String methodName) {
JdbcConfiguration jdbcConfiguration = jdbcConfigurationBuilder.forTestDatabase(testClass, methodName);
return forDatabase(jdbcConfiguration);
}
}