/*
* Copyright (c) 2010-2013 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.repo.sql;
import com.evolveum.midpoint.repo.api.RepositoryServiceFactoryException;
import com.evolveum.midpoint.repo.sql.helpers.OrgClosureManager;
import com.evolveum.midpoint.repo.sql.util.MidPointConnectionCustomizer;
import com.evolveum.midpoint.repo.sql.util.MidPointMySQLDialect;
import com.evolveum.midpoint.repo.sql.util.MidPointPostgreSQLDialect;
import com.evolveum.midpoint.repo.sql.util.UnicodeSQLServer2008Dialect;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.h2.Driver;
import org.hibernate.dialect.*;
/**
* This class is used for SQL repository configuration. It reads values from Apache configuration object (xml).
*
* @author lazyman
*/
public class SqlRepositoryConfiguration {
private static final Trace LOGGER = TraceManager.getTrace(SqlRepositoryConfiguration.class);
enum Database {
H2, MYSQL, POSTGRESQL, SQLSERVER, ORACLE, MARIADB
}
public static final String PROPERTY_DATABASE = "database";
public static final String PROPERTY_BASE_DIR = "baseDir";
public static final String PROPERTY_DROP_IF_EXISTS = "dropIfExists";
public static final String PROPERTY_AS_SERVER = "asServer";
public static final String PROPERTY_PORT = "port";
public static final String PROPERTY_FILE_NAME = "fileName";
public static final String PROPERTY_TCP_SSL = "tcpSSL";
public static final String PROPERTY_EMBEDDED = "embedded";
public static final String PROPERTY_DRIVER_CLASS_NAME = "driverClassName";
public static final String PROPERTY_HIBERNATE_HBM2DDL = "hibernateHbm2ddl";
public static final String PROPERTY_HIBERNATE_DIALECT = "hibernateDialect";
public static final String PROPERTY_JDBC_PASSWORD = "jdbcPassword";
public static final String PROPERTY_JDBC_USERNAME = "jdbcUsername";
public static final String PROPERTY_JDBC_URL = "jdbcUrl";
public static final String PROPERTY_DATASOURCE = "dataSource";
public static final String PROPERTY_USE_ZIP = "useZip";
public static final String PROPERTY_MIN_POOL_SIZE = "minPoolSize";
public static final String PROPERTY_MAX_POOL_SIZE = "maxPoolSize";
// concurrency properties
public static final String PROPERTY_TRANSACTION_ISOLATION = "transactionIsolation";
public static final String PROPERTY_LOCK_FOR_UPDATE_VIA_HIBERNATE = "lockForUpdateViaHibernate";
public static final String PROPERTY_LOCK_FOR_UPDATE_VIA_SQL = "lockForUpdateViaSql";
public static final String PROPERTY_USE_READ_ONLY_TRANSACTIONS = "useReadOnlyTransactions";
public static final String PROPERTY_PERFORMANCE_STATISTICS_FILE = "performanceStatisticsFile";
public static final String PROPERTY_PERFORMANCE_STATISTICS_LEVEL = "performanceStatisticsLevel";
//other
public static final String PROPERTY_ITERATIVE_SEARCH_BY_PAGING = "iterativeSearchByPaging";
public static final String PROPERTY_ITERATIVE_SEARCH_BY_PAGING_BATCH_SIZE = "iterativeSearchByPagingBatchSize";
//closure
public static final String PROPERTY_IGNORE_ORG_CLOSURE = "ignoreOrgClosure";
public static final String PROPERTY_ORG_CLOSURE_STARTUP_ACTION = "orgClosureStartupAction";
public static final String PROPERTY_SKIP_ORG_CLOSURE_STRUCTURE_CHECK = "skipOrgClosureStructureCheck";
public static final String PROPERTY_STOP_ON_ORG_CLOSURE_STARTUP_FAILURE = "stopOnOrgClosureStartupFailure";
private static final String DRIVER_H2 = Driver.class.getName();
private static final String DRIVER_MYSQL = "com.mysql.jdbc.Driver";
private static final String DRIVER_MARIADB = "org.mariadb.jdbc.Driver";
private static final String DRIVER_SQLSERVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
private static final String DRIVER_POSTGRESQL = "org.postgresql.Driver";
private static final String DRIVER_ORACLE = "oracle.jdbc.OracleDriver";
private String database = Database.H2.name();
//embedded configuration
private boolean embedded = true;
private boolean asServer = false;
private String baseDir;
private String fileName;
private boolean tcpSSL;
private int port = 5437;
private boolean dropIfExists;
//connection for hibernate
private String driverClassName;
private String jdbcUrl;
private String jdbcUsername;
private String jdbcPassword;
private String hibernateDialect;
private String hibernateHbm2ddl;
private String dataSource;
private int minPoolSize = 8;
private int maxPoolSize = 20;
private boolean useZip;
private TransactionIsolation transactionIsolation;
private boolean lockForUpdateViaHibernate;
private boolean lockForUpdateViaSql;
private boolean useReadOnlyTransactions;
private String performanceStatisticsFile;
private int performanceStatisticsLevel;
private boolean iterativeSearchByPaging;
private int iterativeSearchByPagingBatchSize;
private boolean ignoreOrgClosure;
private OrgClosureManager.StartupAction orgClosureStartupAction;
private boolean skipOrgClosureStructureCheck;
private boolean stopOnOrgClosureStartupFailure;
public SqlRepositoryConfiguration(Configuration configuration) {
setDatabase(configuration.getString(PROPERTY_DATABASE, database));
computeDefaultDatabaseParameters();
setAsServer(configuration.getBoolean(PROPERTY_AS_SERVER, embedded));
setBaseDir(configuration.getString(PROPERTY_BASE_DIR, baseDir));
setDriverClassName(configuration.getString(PROPERTY_DRIVER_CLASS_NAME, driverClassName));
setEmbedded(configuration.getBoolean(PROPERTY_EMBEDDED, embedded));
setHibernateDialect(configuration.getString(PROPERTY_HIBERNATE_DIALECT, hibernateDialect));
setHibernateHbm2ddl(configuration.getString(PROPERTY_HIBERNATE_HBM2DDL, hibernateHbm2ddl));
setJdbcPassword(configuration.getString(PROPERTY_JDBC_PASSWORD, jdbcPassword));
setJdbcUrl(configuration.getString(PROPERTY_JDBC_URL, jdbcUrl));
setJdbcUsername(configuration.getString(PROPERTY_JDBC_USERNAME, jdbcUsername));
setPort(configuration.getInt(PROPERTY_PORT, port));
setTcpSSL(configuration.getBoolean(PROPERTY_TCP_SSL, tcpSSL));
setFileName(configuration.getString(PROPERTY_FILE_NAME, fileName));
setDropIfExists(configuration.getBoolean(PROPERTY_DROP_IF_EXISTS, dropIfExists));
setDataSource(configuration.getString(PROPERTY_DATASOURCE, null));
setMinPoolSize(configuration.getInt(PROPERTY_MIN_POOL_SIZE, minPoolSize));
setMaxPoolSize(configuration.getInt(PROPERTY_MAX_POOL_SIZE, maxPoolSize));
setUseZip(configuration.getBoolean(PROPERTY_USE_ZIP, useZip));
computeDefaultConcurrencyParameters();
setTransactionIsolation(configuration.getString(PROPERTY_TRANSACTION_ISOLATION, transactionIsolation.value()));
setLockForUpdateViaHibernate(configuration.getBoolean(PROPERTY_LOCK_FOR_UPDATE_VIA_HIBERNATE, lockForUpdateViaHibernate));
setLockForUpdateViaSql(configuration.getBoolean(PROPERTY_LOCK_FOR_UPDATE_VIA_SQL, lockForUpdateViaSql));
setUseReadOnlyTransactions(configuration.getBoolean(PROPERTY_USE_READ_ONLY_TRANSACTIONS, useReadOnlyTransactions));
setPerformanceStatisticsFile(configuration.getString(PROPERTY_PERFORMANCE_STATISTICS_FILE, performanceStatisticsFile));
setPerformanceStatisticsLevel(configuration.getInt(PROPERTY_PERFORMANCE_STATISTICS_LEVEL, performanceStatisticsLevel));
computeDefaultIterativeSearchParameters();
setIterativeSearchByPaging(configuration.getBoolean(PROPERTY_ITERATIVE_SEARCH_BY_PAGING, iterativeSearchByPaging));
setIterativeSearchByPagingBatchSize(configuration.getInt(PROPERTY_ITERATIVE_SEARCH_BY_PAGING_BATCH_SIZE, iterativeSearchByPagingBatchSize));
setIgnoreOrgClosure(configuration.getBoolean(PROPERTY_IGNORE_ORG_CLOSURE, false));
setOrgClosureStartupAction(configuration.getString(PROPERTY_ORG_CLOSURE_STARTUP_ACTION, OrgClosureManager.StartupAction.REBUILD_IF_NEEDED.toString()));
setSkipOrgClosureStructureCheck(configuration.getBoolean(PROPERTY_SKIP_ORG_CLOSURE_STRUCTURE_CHECK, false));
setStopOnOrgClosureStartupFailure(configuration.getBoolean(PROPERTY_STOP_ON_ORG_CLOSURE_STARTUP_FAILURE, true));
}
private void computeDefaultDatabaseParameters() {
if (Database.H2.name().equalsIgnoreCase(getDatabase())) {
embedded = true;
hibernateHbm2ddl = "update";
hibernateDialect = H2Dialect.class.getName();
driverClassName = DRIVER_H2;
} else {
embedded = false;
hibernateHbm2ddl = "validate";
if (Database.MYSQL.name().equalsIgnoreCase(getDatabase())) {
hibernateDialect = MidPointMySQLDialect.class.getName();
driverClassName = DRIVER_MYSQL;
} else if (Database.POSTGRESQL.name().equalsIgnoreCase(getDatabase())) {
hibernateDialect = MidPointPostgreSQLDialect.class.getName();
driverClassName = DRIVER_POSTGRESQL;
} else if (Database.ORACLE.name().equalsIgnoreCase(getDatabase())) {
hibernateDialect = Oracle10gDialect.class.getName();
driverClassName = DRIVER_ORACLE;
} else if (Database.SQLSERVER.name().equalsIgnoreCase(getDatabase())) {
hibernateDialect = UnicodeSQLServer2008Dialect.class.getName();
driverClassName = DRIVER_SQLSERVER;
} else if (Database.MARIADB.name().equalsIgnoreCase(getDatabase())) {
hibernateDialect = MidPointMySQLDialect.class.getName();
driverClassName = DRIVER_MARIADB;
}
}
}
private void computeDefaultConcurrencyParameters() {
if (isUsingH2()) {
transactionIsolation = TransactionIsolation.SERIALIZABLE;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = false;
useReadOnlyTransactions = false; // h2 does not support "SET TRANSACTION READ ONLY" command
} else if (isUsingMySQL() || isUsingMariaDB()) {
transactionIsolation = TransactionIsolation.SERIALIZABLE;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = false;
useReadOnlyTransactions = true;
} else if (isUsingOracle()) {
transactionIsolation = TransactionIsolation.READ_COMMITTED;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = true;
useReadOnlyTransactions = true;
} else if (isUsingPostgreSQL()) {
transactionIsolation = TransactionIsolation.SERIALIZABLE;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = false;
useReadOnlyTransactions = true;
} else if (isUsingSQLServer()) {
transactionIsolation = TransactionIsolation.SNAPSHOT;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = false;
useReadOnlyTransactions = false;
} else {
transactionIsolation = TransactionIsolation.SERIALIZABLE;
lockForUpdateViaHibernate = false;
lockForUpdateViaSql = false;
useReadOnlyTransactions = true;
LOGGER.warn("Fine-tuned concurrency parameters defaults for hibernate dialect " + hibernateDialect
+ " not found; using the following defaults: transactionIsolation = " + transactionIsolation
+ ", lockForUpdateViaHibernate = " + lockForUpdateViaHibernate
+ ", lockForUpdateViaSql = " + lockForUpdateViaSql
+ ", useReadOnlyTransactions = " + useReadOnlyTransactions
+ ". Please override them if necessary.");
}
}
private void computeDefaultIterativeSearchParameters() {
if (isUsingH2()) {
iterativeSearchByPaging = true;
iterativeSearchByPagingBatchSize = 50;
} else if (isUsingMySQL() || isUsingMariaDB()) {
iterativeSearchByPaging = true;
iterativeSearchByPagingBatchSize = 50;
} else {
iterativeSearchByPaging = false;
}
}
/**
* Configuration validation.
*
* @throws RepositoryServiceFactoryException if configuration is invalid.
*/
public void validate() throws RepositoryServiceFactoryException {
if (StringUtils.isEmpty(getDataSource())) {
notEmpty(getJdbcUrl(), "JDBC Url is empty or not defined.");
notEmpty(getJdbcUsername(), "JDBC user name is empty or not defined.");
notNull(getJdbcPassword(), "JDBC password is not defined.");
notEmpty(getDriverClassName(), "Driver class name is empty or not defined.");
}
notEmpty(getHibernateDialect(), "Hibernate dialect is empty or not defined.");
notEmpty(getHibernateHbm2ddl(), "Hibernate hbm2ddl option is empty or not defined.");
if (isEmbedded()) {
notEmpty(getBaseDir(), "Base dir is empty or not defined.");
if (isAsServer()) {
if (getPort() < 0 || getPort() > 65535) {
throw new RepositoryServiceFactoryException("Port must be in interval (0-65534)");
}
}
}
if (getMinPoolSize() <= 0) {
throw new RepositoryServiceFactoryException("Min. pool size must be greater than zero.");
}
if (getMaxPoolSize() <= 0) {
throw new RepositoryServiceFactoryException("Max. pool size must be greater than zero.");
}
if (getMinPoolSize() > getMaxPoolSize()) {
throw new RepositoryServiceFactoryException("Max. pool size must be greater than min. pool size.");
}
}
private void notNull(String value, String message) throws RepositoryServiceFactoryException {
if (value == null) {
throw new RepositoryServiceFactoryException(message);
}
}
private void notEmpty(String value, String message) throws RepositoryServiceFactoryException {
if (StringUtils.isEmpty(value)) {
throw new RepositoryServiceFactoryException(message);
}
}
/**
* @return Returns true if repository is running in embedded server mode, otherwise false. Default is false.
*/
public boolean isAsServer() {
return asServer;
}
public void setAsServer(boolean asServer) {
this.asServer = asServer;
}
public String getBaseDir() {
return baseDir;
}
public void setBaseDir(String baseDir) {
this.baseDir = baseDir;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public boolean isEmbedded() {
return embedded;
}
public void setEmbedded(boolean embedded) {
this.embedded = embedded;
}
/**
* Value represents hibernate dialect used to communicate with database. You can choose from
* <a href="http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/session-configuration.html#configuration-optional-dialects">dialects</a>
* <p/>
* It's used in "hibernate.dialect" property
*
* @return hibernate dialect
*/
public String getHibernateDialect() {
return hibernateDialect;
}
public void setHibernateDialect(String hibernateDialect) {
this.hibernateDialect = hibernateDialect;
}
public String getHibernateHbm2ddl() {
return hibernateHbm2ddl;
}
public void setHibernateHbm2ddl(String hibernateHbm2ddl) {
this.hibernateHbm2ddl = hibernateHbm2ddl;
}
/**
* @return Password for JDBC connection. (Optional)
*/
public String getJdbcPassword() {
return jdbcPassword;
}
public void setJdbcPassword(String jdbcPassword) {
this.jdbcPassword = jdbcPassword;
}
/**
* @return JDBC URL connection string for hibernate data source. (for embedded mode it's created automatically).
*/
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
/**
* @return Username for JDBC connection. (Optional)
*/
public String getJdbcUsername() {
return jdbcUsername;
}
public void setJdbcUsername(String jdbcUsername) {
this.jdbcUsername = jdbcUsername;
}
/**
* @return Port number if repository is running in embedded server mode. Default is 5437.
*/
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
/**
* Value represents repository running in embedded server mode with SSL turned on/off. Default value is false.
*
* @return Returns true if repository is running in embedded server mode and SSL turned on.
*/
public boolean isTcpSSL() {
return tcpSSL;
}
public void setTcpSSL(boolean tcpSSL) {
this.tcpSSL = tcpSSL;
}
/**
* Used in embedded mode to define h2 database file name. Default will be "midpoint".
*
* @return name of DB file
*/
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public boolean isDropIfExists() {
return dropIfExists;
}
public void setDropIfExists(boolean dropIfExists) {
this.dropIfExists = dropIfExists;
}
public TransactionIsolation getTransactionIsolation() {
return transactionIsolation;
}
public void setTransactionIsolation(TransactionIsolation transactionIsolation) {
this.transactionIsolation = transactionIsolation;
}
public void setTransactionIsolation(String transactionIsolation) {
this.transactionIsolation = TransactionIsolation.fromValue(transactionIsolation);
// ugly hack, but I know of no way to work around
MidPointConnectionCustomizer.setTransactionIsolation(this.transactionIsolation);
}
public boolean isLockForUpdateViaHibernate() {
return lockForUpdateViaHibernate;
}
public void setLockForUpdateViaHibernate(boolean lockForUpdateViaHibernate) {
this.lockForUpdateViaHibernate = lockForUpdateViaHibernate;
}
public boolean isLockForUpdateViaSql() {
return lockForUpdateViaSql;
}
public void setLockForUpdateViaSql(boolean lockForUpdateViaSql) {
this.lockForUpdateViaSql = lockForUpdateViaSql;
}
public boolean isUseReadOnlyTransactions() {
return useReadOnlyTransactions;
}
public void setUseReadOnlyTransactions(boolean useReadOnlyTransactions) {
this.useReadOnlyTransactions = useReadOnlyTransactions;
}
public String getPerformanceStatisticsFile() {
return performanceStatisticsFile;
}
public void setPerformanceStatisticsFile(String performanceStatisticsFile) {
this.performanceStatisticsFile = performanceStatisticsFile;
}
public int getPerformanceStatisticsLevel() {
return performanceStatisticsLevel;
}
public void setPerformanceStatisticsLevel(int performanceStatisticsLevel) {
this.performanceStatisticsLevel = performanceStatisticsLevel;
}
public boolean isIterativeSearchByPaging() {
return iterativeSearchByPaging;
}
public void setIterativeSearchByPaging(boolean iterativeSearchByPaging) {
this.iterativeSearchByPaging = iterativeSearchByPaging;
}
public int getIterativeSearchByPagingBatchSize() {
return iterativeSearchByPagingBatchSize;
}
public void setIterativeSearchByPagingBatchSize(int iterativeSearchByPagingBatchSize) {
this.iterativeSearchByPagingBatchSize = iterativeSearchByPagingBatchSize;
}
public String getDataSource() {
return dataSource;
}
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
}
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 boolean isUseZip() {
return useZip;
}
public void setUseZip(boolean useZip) {
this.useZip = useZip;
}
public boolean isIgnoreOrgClosure() {
return ignoreOrgClosure;
}
public void setIgnoreOrgClosure(boolean value) {
this.ignoreOrgClosure = value;
}
public OrgClosureManager.StartupAction getOrgClosureStartupAction() {
return orgClosureStartupAction;
}
public void setOrgClosureStartupAction(String orgClosureStartupAction) {
this.orgClosureStartupAction = OrgClosureManager.StartupAction.fromValue(orgClosureStartupAction);
}
public boolean isUsingH2() {
return DRIVER_H2.equals(driverClassName);
}
public boolean isUsingOracle() {
return DRIVER_ORACLE.equals(driverClassName);
}
public boolean isUsingMySQL() {
return DRIVER_MYSQL.equals(driverClassName);
}
public boolean isUsingMariaDB() {
return DRIVER_MARIADB.equals(driverClassName);
}
public boolean isUsingPostgreSQL() {
return DRIVER_POSTGRESQL.equals(driverClassName);
}
public boolean isUsingSQLServer() {
return DRIVER_SQLSERVER.equals(driverClassName);
}
public void setStopOnOrgClosureStartupFailure(boolean stopOnOrgClosureStartupFailure) {
this.stopOnOrgClosureStartupFailure = stopOnOrgClosureStartupFailure;
}
public boolean isStopOnOrgClosureStartupFailure() {
return stopOnOrgClosureStartupFailure;
}
public boolean isSkipOrgClosureStructureCheck() {
return skipOrgClosureStructureCheck;
}
public void setSkipOrgClosureStructureCheck(boolean skipOrgClosureStructureCheck) {
this.skipOrgClosureStructureCheck = skipOrgClosureStructureCheck;
}
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
}