/* * HA-JDBC: High-Availability JDBC * Copyright (C) 2014 Paul Ferraro * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.hajdbc; import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import net.sf.hajdbc.balancer.BalancerFactory; import net.sf.hajdbc.balancer.load.LoadBalancerFactory; import net.sf.hajdbc.cache.DatabaseMetaDataCacheFactory; import net.sf.hajdbc.cache.eager.EagerDatabaseMetaDataCacheFactory; import net.sf.hajdbc.codec.DecoderFactory; import net.sf.hajdbc.codec.MultiplexingDecoderFactory; import net.sf.hajdbc.configuration.Builder; import net.sf.hajdbc.configuration.ServiceBuilder; import net.sf.hajdbc.configuration.SimpleBuilder; import net.sf.hajdbc.configuration.SimpleServiceBuilder; import net.sf.hajdbc.dialect.DialectFactory; import net.sf.hajdbc.dialect.StandardDialectFactory; import net.sf.hajdbc.distributed.CommandDispatcherFactory; import net.sf.hajdbc.durability.DurabilityFactory; import net.sf.hajdbc.durability.coarse.CoarseDurabilityFactory; import net.sf.hajdbc.io.InputSinkProvider; import net.sf.hajdbc.io.file.FileInputSinkProvider; import net.sf.hajdbc.lock.LockManagerFactory; import net.sf.hajdbc.lock.semaphore.SemaphoreLockManagerFactory; import net.sf.hajdbc.management.DefaultMBeanRegistrarFactory; import net.sf.hajdbc.management.MBeanRegistrarFactory; import net.sf.hajdbc.messages.Messages; import net.sf.hajdbc.messages.MessagesFactory; import net.sf.hajdbc.sql.DefaultExecutorServiceProvider; import net.sf.hajdbc.sql.TransactionModeEnum; import net.sf.hajdbc.state.StateManagerFactory; import net.sf.hajdbc.state.sql.SQLStateManagerFactory; import net.sf.hajdbc.util.concurrent.cron.CronExpression; import net.sf.hajdbc.util.concurrent.cron.CronExpressionBuilder; public class DatabaseClusterConfigurationBuilder<Z, D extends Database<Z>, B extends DatabaseBuilder<Z, D>> implements Builder<DatabaseClusterConfiguration<Z, D>> { private static final Messages messages = MessagesFactory.getMessages(); private final DatabaseBuilderFactory<Z, D, B> factory; private final List<Builder<SynchronizationStrategy>> synchronizationStrategyBuilders = new LinkedList<>(); private final List<Builder<D>> databaseBuilders = new LinkedList<>(); private volatile Builder<CommandDispatcherFactory> commandDispatcherFactoryBuilder; private volatile Builder<StateManagerFactory> stateManagerFactoryBuilder = new SimpleBuilder<>(new SQLStateManagerFactory()); private volatile Builder<LockManagerFactory> lockManagerFactoryBuilder = new SimpleBuilder<>(new SemaphoreLockManagerFactory()); private volatile Builder<BalancerFactory> balancerFactoryBuilder = new SimpleBuilder<>(new LoadBalancerFactory()); private volatile Builder<DialectFactory> dialectFactoryBuilder = new SimpleBuilder<>(new StandardDialectFactory()); private volatile Builder<DurabilityFactory> durabilityFactoryBuilder = new SimpleBuilder<>(new CoarseDurabilityFactory()); private volatile Builder<InputSinkProvider> inputSinkProviderBuilder = new SimpleBuilder<>(new FileInputSinkProvider()); private volatile Builder<DatabaseMetaDataCacheFactory> metaDataCacheFactoryBuilder = new SimpleBuilder<>(new EagerDatabaseMetaDataCacheFactory()); private volatile Builder<DecoderFactory> decoderFactoryBuilder = new SimpleBuilder<>(new MultiplexingDecoderFactory()); private volatile Builder<MBeanRegistrarFactory> mbeanRegistrarFactoryBuilder = new SimpleBuilder<>(new DefaultMBeanRegistrarFactory()); private volatile Builder<ThreadFactory> threadFactoryBuilder = new SimpleBuilder<>(Executors.defaultThreadFactory()); private volatile Builder<ExecutorServiceProvider> executorProviderBuilder = new SimpleBuilder<>(new DefaultExecutorServiceProvider()); private volatile CronExpressionBuilder autoActivateScheduleBuilder = new CronExpressionBuilder(); private volatile CronExpressionBuilder failureDetectScheduleBuilder = new CronExpressionBuilder(); private volatile String defaultSynchronizationStrategy; private volatile TransactionMode transactionMode = TransactionModeEnum.SERIAL; private volatile boolean evalCurrentDate = false; private volatile boolean evalCurrentTime = false; private volatile boolean evalCurrentTimestamp = false; private volatile boolean evalRand = false; private volatile boolean detectIdentityColumns = false; private volatile boolean detectSequences = false; private volatile boolean allowEmptyCluster = false; protected DatabaseClusterConfigurationBuilder(DatabaseBuilderFactory<Z, D, B> factory) { this.factory = factory; } public ServiceBuilder<CommandDispatcherFactory> distributable(String id) { ServiceBuilder<CommandDispatcherFactory> builder = new ServiceBuilder<>(CommandDispatcherFactory.class, id); this.commandDispatcherFactoryBuilder = builder; return builder; } public <T extends Builder<CommandDispatcherFactory>> T distributable(Class<T> builderClass) { try { T builder = builderClass.newInstance(); this.commandDispatcherFactoryBuilder = builder; return builder; } catch (IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } public DatabaseClusterConfigurationBuilder<Z, D, B> distributable(CommandDispatcherFactory factory) { this.commandDispatcherFactoryBuilder = new SimpleBuilder<>(factory); return this; } public ServiceBuilder<StateManagerFactory> state(String id) { ServiceBuilder<StateManagerFactory> builder = new ServiceBuilder<>(StateManagerFactory.class, id); this.stateManagerFactoryBuilder = builder; return builder; } public <T extends Builder<StateManagerFactory>> T state(Class<T> builderClass) { try { T builder = builderClass.newInstance(); this.stateManagerFactoryBuilder = builder; return builder; } catch (IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } public DatabaseClusterConfigurationBuilder<Z, D, B> state(StateManagerFactory factory) { this.stateManagerFactoryBuilder = new SimpleBuilder<>(factory); return this; } public ServiceBuilder<LockManagerFactory> lock(String id) { ServiceBuilder<LockManagerFactory> builder = new ServiceBuilder<>(LockManagerFactory.class, id); this.lockManagerFactoryBuilder = builder; return builder; } public <T extends Builder<LockManagerFactory>> T lock(Class<T> builderClass) { try { T builder = builderClass.newInstance(); this.lockManagerFactoryBuilder = builder; return builder; } catch (IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } public DatabaseClusterConfigurationBuilder<Z, D, B> lock(LockManagerFactory factory) { this.lockManagerFactoryBuilder = new SimpleBuilder<>(factory); return this; } public ServiceBuilder<SynchronizationStrategy> addSynchronizationStrategy(String id) { ServiceBuilder<SynchronizationStrategy> builder = new ServiceBuilder<>(SynchronizationStrategy.class, id); this.synchronizationStrategyBuilders.add(builder); return builder; } public <T extends Builder<SynchronizationStrategy>> T addSynchronizationStrategy(Class<T> builderClass) { try { T builder = builderClass.newInstance(); this.synchronizationStrategyBuilders.add(builder); return builder; } catch (IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } public DatabaseClusterConfigurationBuilder<Z, D, B> addSynchronizationStrategy(SynchronizationStrategy strategy) { this.synchronizationStrategyBuilders.add(new SimpleBuilder<>(strategy)); return this; } public B addDatabase(String id) { B builder = this.factory.createBuilder(id); this.databaseBuilders.add(builder); return builder; } public <T extends Builder<ExecutorServiceProvider>> T executor(Class<T> builderClass) { try { T builder = builderClass.newInstance(); this.executorProviderBuilder = builder; return builder; } catch (IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } public DatabaseClusterConfigurationBuilder<Z, D, B> executor(ExecutorServiceProvider provider) { this.executorProviderBuilder = new SimpleBuilder<>(provider); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> balancer(String id) { this.balancerFactoryBuilder = new SimpleServiceBuilder<>(BalancerFactory.class, id); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> balancer(BalancerFactory factory) { this.balancerFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> dialect(String id) { this.dialectFactoryBuilder = new SimpleServiceBuilder<>(DialectFactory.class, id); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> dialect(DialectFactory factory) { this.dialectFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> durability(String id) { this.durabilityFactoryBuilder = new SimpleServiceBuilder<>(DurabilityFactory.class, id); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> durability(DurabilityFactory factory) { this.durabilityFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> metaDataCache(String id) { this.metaDataCacheFactoryBuilder = new SimpleServiceBuilder<>(DatabaseMetaDataCacheFactory.class, id); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> metaDataCache(DatabaseMetaDataCacheFactory factory) { this.metaDataCacheFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> inputSink(String id) { this.inputSinkProviderBuilder = new SimpleServiceBuilder<>(InputSinkProvider.class, id); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> inputSink(InputSinkProvider factory) { this.inputSinkProviderBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> decoder(DecoderFactory factory) { this.decoderFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> mbeanRegistrar(MBeanRegistrarFactory factory) { this.mbeanRegistrarFactoryBuilder = new SimpleBuilder<>(factory); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> defaultSynchronizationStrategy(String id) { this.defaultSynchronizationStrategy = id; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> transactionMode(TransactionMode transactionMode) { this.transactionMode = transactionMode; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> autoActivateSchedule(String schedule) { this.autoActivateScheduleBuilder.expression(schedule); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> failureDetectSchedule(String schedule) { this.failureDetectScheduleBuilder.expression(schedule); return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> evalCurrentDate(boolean enabled) { this.evalCurrentDate = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> evalCurrentTime(boolean enabled) { this.evalCurrentTime = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> evalCurrentTimestamp(boolean enabled) { this.evalCurrentTimestamp = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> evalRand(boolean enabled) { this.evalRand = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> detectIdentityColumns(boolean enabled) { this.detectIdentityColumns = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> detectSequences(boolean enabled) { this.detectSequences = enabled; return this; } public DatabaseClusterConfigurationBuilder<Z, D, B> allowEmptyCluster(boolean enabled) { this.allowEmptyCluster = enabled; return this; } @Override public DatabaseClusterConfigurationBuilder<Z, D, B> read(DatabaseClusterConfiguration<Z, D> configuration) { return this; } @Override public DatabaseClusterConfiguration<Z, D> build() throws SQLException { final CommandDispatcherFactory commandDispatcherFactory = (this.commandDispatcherFactoryBuilder != null) ? this.commandDispatcherFactoryBuilder.build() : null; final StateManagerFactory stateManagerFactory = this.stateManagerFactoryBuilder.build(); final LockManagerFactory lockManagerFactory = this.lockManagerFactoryBuilder.build(); final BalancerFactory balancerFactory = this.balancerFactoryBuilder.build(); final DialectFactory dialectFactory = this.dialectFactoryBuilder.build(); final DurabilityFactory durabilityFactory = this.durabilityFactoryBuilder.build(); final InputSinkProvider inputSinkProvider = this.inputSinkProviderBuilder.build(); final DatabaseMetaDataCacheFactory metaDataCacheFactory = this.metaDataCacheFactoryBuilder.build(); final DecoderFactory decoderFactory = this.decoderFactoryBuilder.build(); final MBeanRegistrarFactory mbeanRegistrarFactory = this.mbeanRegistrarFactoryBuilder.build(); final ThreadFactory threadFactory = this.threadFactoryBuilder.build(); final ExecutorServiceProvider executorServiceProvider = this.executorProviderBuilder.build(); final CronExpression autoActivateSchedule = this.autoActivateScheduleBuilder.build(); final CronExpression failureDetectSchedule = this.failureDetectScheduleBuilder.build(); final String defaultSynchronizationStrategy = this.defaultSynchronizationStrategy; final TransactionMode transactionMode = this.transactionMode; final boolean evalCurrentDate = this.evalCurrentDate; final boolean evalCurrentTime = this.evalCurrentTime; final boolean evalCurrentTimestamp = this.evalCurrentTimestamp; final boolean evalRand = this.evalRand; final boolean detectIdentityColumns = this.detectIdentityColumns; final boolean detectSequences = this.detectSequences; final boolean allowEmptyCluster = this.allowEmptyCluster; if (this.synchronizationStrategyBuilders.isEmpty()) { throw new SQLException(messages.noSyncStrategies()); } final Map<String, SynchronizationStrategy> syncStrategies = new HashMap<>(); for (Builder<SynchronizationStrategy> builder: this.synchronizationStrategyBuilders) { SynchronizationStrategy strategy = builder.build(); syncStrategies.put(strategy.getId(), strategy); } if (!syncStrategies.containsKey(defaultSynchronizationStrategy)) { throw new SQLException(messages.invalidSyncStrategy(defaultSynchronizationStrategy, syncStrategies.keySet())); } if (this.databaseBuilders.isEmpty()) { throw new SQLException(messages.noDatabases()); } final ConcurrentMap<String, D> databases = new ConcurrentHashMap<>(); for (Builder<D> builder: this.databaseBuilders) { D database = builder.build(); databases.put(database.getId(), database); } return new DatabaseClusterConfiguration<Z, D>() { @Override public CommandDispatcherFactory getDispatcherFactory() { return commandDispatcherFactory; } @Override public ConcurrentMap<String, D> getDatabaseMap() { return databases; } @Override public Map<String, SynchronizationStrategy> getSynchronizationStrategyMap() { return syncStrategies; } @Override public String getDefaultSynchronizationStrategy() { return defaultSynchronizationStrategy; } @Override public BalancerFactory getBalancerFactory() { return balancerFactory; } @Override public TransactionMode getTransactionMode() { return transactionMode; } @Override public ExecutorServiceProvider getExecutorProvider() { return executorServiceProvider; } @Override public DialectFactory getDialectFactory() { return dialectFactory; } @Override public StateManagerFactory getStateManagerFactory() { return stateManagerFactory; } @Override public DatabaseMetaDataCacheFactory getDatabaseMetaDataCacheFactory() { return metaDataCacheFactory; } @Override public DurabilityFactory getDurabilityFactory() { return durabilityFactory; } @Override public LockManagerFactory getLockManagerFactory() { return lockManagerFactory; } @Override public boolean isSequenceDetectionEnabled() { return detectSequences; } @Override public boolean isIdentityColumnDetectionEnabled() { return detectIdentityColumns; } @Override public boolean isCurrentDateEvaluationEnabled() { return evalCurrentDate; } @Override public boolean isCurrentTimeEvaluationEnabled() { return evalCurrentTime; } @Override public boolean isCurrentTimestampEvaluationEnabled() { return evalCurrentTimestamp; } @Override public boolean isRandEvaluationEnabled() { return evalRand; } @Override public CronExpression getFailureDetectionExpression() { return failureDetectSchedule; } @Override public CronExpression getAutoActivationExpression() { return autoActivateSchedule; } @Override public ThreadFactory getThreadFactory() { return threadFactory; } @Override public DecoderFactory getDecoderFactory() { return decoderFactory; } @Override public MBeanRegistrarFactory getMBeanRegistrarFactory() { return mbeanRegistrarFactory; } @Override public boolean isEmptyClusterAllowed() { return allowEmptyCluster; } @Override public InputSinkProvider getInputSinkProvider() { return inputSinkProvider; } }; } }