All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.hajdbc.sync.SynchronizationSupportImpl Maven / Gradle / Ivy

There is a newer version: 3.6.61
Show newest version
/*
 * HA-JDBC: High-Availability JDBC
 * Copyright (C) 2012  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 .
 */
package net.sf.hajdbc.sync;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import net.sf.hajdbc.Database;
import net.sf.hajdbc.ExceptionType;
import net.sf.hajdbc.ForeignKeyConstraint;
import net.sf.hajdbc.IdentityColumnSupport;
import net.sf.hajdbc.Messages;
import net.sf.hajdbc.SequenceProperties;
import net.sf.hajdbc.SequenceSupport;
import net.sf.hajdbc.TableProperties;
import net.sf.hajdbc.UniqueConstraint;
import net.sf.hajdbc.dialect.Dialect;
import net.sf.hajdbc.logging.Level;
import net.sf.hajdbc.logging.Logger;
import net.sf.hajdbc.logging.LoggerFactory;
import net.sf.hajdbc.util.Resources;
import net.sf.hajdbc.util.Strings;

/**
 * Default {@link SynchronizationSupport} implementation.
 * @author Paul Ferraro
 */
public class SynchronizationSupportImpl> implements SynchronizationSupport
{
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private final SynchronizationContext context;
	
	public SynchronizationSupportImpl(SynchronizationContext context)
	{
		this.context = context;
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#dropForeignKeys()
	 */
	@Override
	public void dropForeignKeys() throws SQLException
	{
		Dialect dialect = this.context.getDialect();
		
		Connection connection = this.context.getConnection(this.context.getTargetDatabase());
		boolean autoCommit = connection.getAutoCommit();
		try
		{
			connection.setAutoCommit(true);
			
			Statement statement = connection.createStatement();
			try
			{
				for (TableProperties table: this.context.getTargetDatabaseProperties().getTables())
				{
					for (ForeignKeyConstraint constraint: table.getForeignKeyConstraints())
					{
						String sql = dialect.getDropForeignKeyConstraintSQL(constraint);
						
						this.logger.log(Level.DEBUG, sql);
						
						statement.addBatch(sql);
					}
				}
				statement.executeBatch();
			}
			finally
			{
				Resources.close(statement);
			}
		}
		finally
		{
			connection.setAutoCommit(autoCommit);
		}
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#restoreForeignKeys()
	 */
	@Override
	public void restoreForeignKeys() throws SQLException
	{
		Dialect dialect = this.context.getDialect();
		
		Connection connection = this.context.getConnection(this.context.getTargetDatabase());
		boolean autoCommit = connection.getAutoCommit();
		try
		{
			connection.setAutoCommit(true);
			
			Statement statement = connection.createStatement();
			try
			{
				for (TableProperties table: this.context.getSourceDatabaseProperties().getTables())
				{
					for (ForeignKeyConstraint constraint: table.getForeignKeyConstraints())
					{
						String sql = dialect.getCreateForeignKeyConstraintSQL(constraint);
						
						this.logger.log(Level.DEBUG, sql);
						
						statement.addBatch(sql);
					}
				}
				
				statement.executeBatch();
			}
			finally
			{
				Resources.close(statement);
			}
		}
		finally
		{
			connection.setAutoCommit(autoCommit);
		}
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#synchronizeSequences()
	 */
	@Override
	public void synchronizeSequences() throws SQLException
	{
		SequenceSupport support = this.context.getDialect().getSequenceSupport();
		
		if (support != null)
		{
			Collection sequences = this.context.getSourceDatabaseProperties().getSequences();

			if (!sequences.isEmpty())
			{
				D sourceDatabase = this.context.getSourceDatabase();
				
				Set databases = this.context.getActiveDatabaseSet();

				ExecutorService executor = this.context.getExecutor();
				
				Map sequenceMap = new HashMap();
				Map> futureMap = new HashMap>();

				for (SequenceProperties sequence: sequences)
				{
					final String sql = support.getNextSequenceValueSQL(sequence);
					
					this.logger.log(Level.DEBUG, sql);

					for (final D database: databases)
					{
						final SynchronizationContext context = this.context;
						
						Callable task = new Callable()
						{
							@Override
							public Long call() throws SQLException
							{
								Statement statement = context.getConnection(database).createStatement();
								try
								{
									ResultSet resultSet = statement.executeQuery(sql);
									
									resultSet.next();
									
									return resultSet.getLong(1);
								}
								finally
								{
									Resources.close(statement);
								}
							}
						};
						
						futureMap.put(database, executor.submit(task));
					}

					try
					{
						Long sourceValue = futureMap.get(sourceDatabase).get();
						
						sequenceMap.put(sequence, sourceValue);
						
						for (D database: databases)
						{
							if (!database.equals(sourceDatabase))
							{
								Long value = futureMap.get(database).get();
								
								if (!value.equals(sourceValue))
								{
									throw new SQLException(Messages.SEQUENCE_OUT_OF_SYNC.getMessage(sequence, database, value, sourceDatabase, sourceValue));
								}
							}
						}
					}
					catch (InterruptedException e)
					{
						throw new SQLException(e);
					}
					catch (ExecutionException e)
					{
						throw ExceptionType.SQL.getExceptionFactory().createException(e.getCause());
					}
				}
				
				Connection targetConnection = this.context.getConnection(this.context.getTargetDatabase());
				Statement targetStatement = targetConnection.createStatement();
				try
				{
					for (SequenceProperties sequence: sequences)
					{
						String sql = support.getAlterSequenceSQL(sequence, sequenceMap.get(sequence) + 1);
						
						this.logger.log(Level.DEBUG, sql);
						
						targetStatement.addBatch(sql);
					}
					
					targetStatement.executeBatch();
				}
				finally
				{
					Resources.close(targetStatement);
				}
			}
		}
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#synchronizeIdentityColumns()
	 */
	@Override
	public void synchronizeIdentityColumns() throws SQLException
	{
		IdentityColumnSupport support = this.context.getDialect().getIdentityColumnSupport();
		
		if (support != null)
		{
			Statement sourceStatement = this.context.getConnection(this.context.getSourceDatabase()).createStatement();
			try
			{
				Statement targetStatement = this.context.getConnection(this.context.getTargetDatabase()).createStatement();
				try
				{
					for (TableProperties table: this.context.getSourceDatabaseProperties().getTables())
					{
						Collection columns = table.getIdentityColumns();
						
						if (!columns.isEmpty())
						{
							String selectSQL = MessageFormat.format("SELECT max({0}) FROM {1}", Strings.join(columns, "), max("), table.getName()); //$NON-NLS-1$ //$NON-NLS-2$
							
							this.logger.log(Level.DEBUG, selectSQL);
							
							Map map = new HashMap();
							ResultSet resultSet = sourceStatement.executeQuery(selectSQL);
							try
							{
								if (resultSet.next())
								{
									int i = 0;
									
									for (String column: columns)
									{
										map.put(column, resultSet.getLong(++i));
									}
								}
							}
							finally
							{
								Resources.close(resultSet);
							}
							
							if (!map.isEmpty())
							{
								for (Map.Entry mapEntry: map.entrySet())
								{
									String alterSQL = support.getAlterIdentityColumnSQL(table, table.getColumnProperties(mapEntry.getKey()), mapEntry.getValue() + 1);
									
									if (alterSQL != null)
									{
										this.logger.log(Level.DEBUG, alterSQL);
										
										targetStatement.addBatch(alterSQL);
									}
								}
								
								targetStatement.executeBatch();
							}
						}
					}
				}
				finally
				{
					Resources.close(targetStatement);
				}
			}
			finally
			{
				Resources.close(sourceStatement);
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#dropUniqueConstraints()
	 */
	@Override
	public void dropUniqueConstraints() throws SQLException
	{
		Dialect dialect = this.context.getDialect();

		Connection connection = this.context.getConnection(this.context.getTargetDatabase());
		boolean autoCommit = connection.getAutoCommit();
		try
		{
			connection.setAutoCommit(true);
			Statement statement = connection.createStatement();
			try
			{
				for (TableProperties table: this.context.getTargetDatabaseProperties().getTables())
				{
					for (UniqueConstraint constraint: table.getUniqueConstraints())
					{
						String sql = dialect.getDropUniqueConstraintSQL(constraint);
						
						this.logger.log(Level.DEBUG, sql);
						
						statement.addBatch(sql);
					}
				}
				
				statement.executeBatch();
			}
			finally
			{
				Resources.close(statement);
			}
		}
		finally
		{
			connection.setAutoCommit(autoCommit);
		}
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#restoreUniqueConstraints()
	 */
	@Override
	public void restoreUniqueConstraints() throws SQLException
	{
		Dialect dialect = this.context.getDialect();

		Connection connection = this.context.getConnection(this.context.getTargetDatabase());
		boolean autoCommit = connection.getAutoCommit();
		try
		{
			connection.setAutoCommit(true);
			
			Statement statement = connection.createStatement();
			try
			{
				for (TableProperties table: this.context.getSourceDatabaseProperties().getTables())
				{
					// Drop unique constraints on the current table
					for (UniqueConstraint constraint: table.getUniqueConstraints())
					{
						String sql = dialect.getCreateUniqueConstraintSQL(constraint);
						
						this.logger.log(Level.DEBUG, sql);
						
						statement.addBatch(sql);
					}
				}
				
				statement.executeBatch();
			}
			finally
			{
				Resources.close(statement);
			}
		}
		finally
		{
			connection.setAutoCommit(autoCommit);
		}
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#rollback(java.sql.Connection)
	 */
	@Override
	public void rollback(Connection connection)
	{
		try
		{
			connection.rollback();
		}
		catch (SQLException e)
		{
			this.logger.log(Level.WARN, e);
		}
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.sync.SynchronizationSupport#getObject(java.sql.ResultSet, int, int)
	 */
	@Override
	public Object getObject(ResultSet resultSet, int index, int type) throws SQLException
	{
		switch (type)
		{
			case Types.BLOB:
			{
				return resultSet.getBlob(index);
			}
			case Types.CLOB:
			{
				return resultSet.getClob(index);
			}
			default:
			{
				return resultSet.getObject(index);
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy