blob: 242689496c1e18f11b307016b982f830b7f7f26c [file] [log] [blame]
package org.apache.ddlutils.task;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ddlutils.Platform;
import org.apache.ddlutils.model.Database;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
/**
* Base class for DdlUtils Ant tasks that operate on a database.
*
* @version $Revision: 289996 $
* @ant.task ignore="true"
*/
public abstract class DatabaseTaskBase extends Task
{
/** The log. */
protected Log _log;
/** The platform configuration. */
private PlatformConfiguration _platformConf = new PlatformConfiguration();
/** The sub tasks to execute. */
private ArrayList _commands = new ArrayList();
/** Whether to use simple logging (that the Ant task configures itself via the {@link #_verbosity} setting. */
private boolean _simpleLogging = true;
/** The verbosity of the task's debug output. */
private VerbosityLevel _verbosity = null;
/**
* Specifies whether simple logging (configured by the task via the <code>verbosity</code>
* setting) shall be used, or whether logging is configured outside of the task
* (e.g. via a log4j properties file).
*
* @param simpleLogging Whether to use simple logging or not
* @ant.not-required Per default, simple logging is enabled.
*/
public void setSimpleLogging(boolean simpleLogging)
{
_simpleLogging = simpleLogging;
}
/**
* Specifies the verbosity of the task's debug output.
*
* @param level The verbosity level
* @ant.not-required Default is <code>INFO</code>.
*/
public void setVerbosity(VerbosityLevel level)
{
_verbosity = level;
}
/**
* Returns the database type.
*
* @return The database type
*/
public String getDatabaseType()
{
return _platformConf.getDatabaseType();
}
/**
* Specifies the database type. You should only need to set this if DdlUtils is not able to
* derive the setting from the name of the used jdbc driver or the jdbc connection url.
* If you have to specify this, please post your jdbc driver and connection url combo
* to the user mailing list so that DdlUtils can be enhanced to support this combo.<br/>
* Valid values are currently:<br/><code>axion, cloudscape, db2, derby, firebird, hsqldb, interbase,
* maxdb, mckoi, mssql, mysql, mysql5, oracle, oracle9, oracle10, postgresql, sapdb, sybase</code>
*
* @param type The database type
* @ant.not-required Per default, DdlUtils tries to determine the database type via JDBC.
*/
public void setDatabaseType(String type)
{
if ((type != null) && (type.length() > 0))
{
_platformConf.setDatabaseType(type);
}
}
/**
* Returns the data source.
*
* @return The data source
*/
public BasicDataSource getDataSource()
{
return _platformConf.getDataSource();
}
/**
* Adds the data source to use for accessing the database.
*
* @param dataSource The data source
*/
public void addConfiguredDatabase(BasicDataSource dataSource)
{
_platformConf.setDataSource(dataSource);
}
/**
* Specifies a pattern that defines which database catalogs to use. For some
* more info on catalog patterns and JDBC, see
* <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html">java.sql.DatabaseMetaData</a>.
*
* @param catalogPattern The catalog pattern
* @ant.not-required Per default no specific catalog is used.
*/
public void setCatalogPattern(String catalogPattern)
{
if ((catalogPattern != null) && (catalogPattern.length() > 0))
{
_platformConf.setCatalogPattern(catalogPattern);
}
}
/**
* Specifies a pattern that defines which database schemas to use. For some
* more info on schema patterns and JDBC, see
* <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html">java.sql.DatabaseMetaData</a>.
*
* @param schemaPattern The schema pattern
* @ant.not-required Per default no specific schema is used.
*/
public void setSchemaPattern(String schemaPattern)
{
if ((schemaPattern != null) && (schemaPattern.length() > 0))
{
_platformConf.setSchemaPattern(schemaPattern);
}
}
/**
* Determines whether delimited SQL identifiers shall be used (the default).
*
* @return <code>true</code> if delimited SQL identifiers shall be used
*/
public boolean isUseDelimitedSqlIdentifiers()
{
return _platformConf.isUseDelimitedSqlIdentifiers();
}
/**
* Specifies whether DdlUtils shall use delimited (quoted) identifiers (such as table and column
* names). Most databases convert undelimited identifiers to uppercase and ignore the case of
* identifiers when performing any SQL command. Undelimited identifiers also cannot be reserved
* words and can only contain alphanumerical characters and the underscore.<br/>
* These limitations do not exist for delimited identifiers where identifiers have to be enclosed
* in double quotes. Delimited identifiers can contain unicode characters, and even reserved
* words can be used as identifiers. Please be aware though, that they always have to enclosed
* in double quotes, and that the case of the identifier will be important in every SQL command
* executed against the database.
*
* @param useDelimitedSqlIdentifiers <code>true</code> if delimited SQL identifiers shall be used
* @ant.not-required Default is <code>false</code>.
*/
public void setUseDelimitedSqlIdentifiers(boolean useDelimitedSqlIdentifiers)
{
_platformConf.setUseDelimitedSqlIdentifiers(useDelimitedSqlIdentifiers);
}
/**
* Determines whether a table's foreign keys read from a live database
* shall be sorted alphabetically. Is <code>false</code> by default.
*
* @return <code>true</code> if the foreign keys shall be sorted
*/
public boolean isSortForeignKeys()
{
return _platformConf.isSortForeignKeys();
}
/**
* Specifies whether DdlUtils shall sort the foreign keys of a table read from a live database or
* leave them in the order in which they are returned by the database/JDBC driver. Note that
* the sort is case sensitive only if delimited identifier mode is on
* (<code>useDelimitedSqlIdentifiers</code> is set to <code>true</code>).
*
* @param sortForeignKeys <code>true</code> if the foreign keys shall be sorted
* @ant.not-required Default is <code>false</code>.
*/
public void setSortForeignKeys(boolean sortForeignKeys)
{
_platformConf.setSortForeignKeys(sortForeignKeys);
}
/**
* Determines whether the database shall be shut down after the task has finished.
*
* @return <code>true</code> if the database shall be shut down
*/
public boolean isShutdownDatabase()
{
return _platformConf.isShutdownDatabase();
}
/**
* Specifies whether DdlUtils shall shut down the database after the task has finished.
* This is mostly useful for embedded databases.
*
* @param shutdownDatabase <code>true</code> if the database shall be shut down
* @ant.not-required Default is <code>false</code>.
*/
public void setShutdownDatabase(boolean shutdownDatabase)
{
_platformConf.setShutdownDatabase(shutdownDatabase);
}
/**
* Adds a command.
*
* @param command The command
*/
protected void addCommand(Command command)
{
_commands.add(command);
}
/**
* Determines whether there are commands to perform.
*
* @return <code>true</code> if there are commands
*/
protected boolean hasCommands()
{
return !_commands.isEmpty();
}
/**
* Returns the commands.
*
* @return The commands
*/
protected Iterator getCommands()
{
return _commands.iterator();
}
/**
* Creates the platform configuration.
*
* @return The platform configuration
*/
protected PlatformConfiguration getPlatformConfiguration()
{
return _platformConf;
}
/**
* Creates the platform for the configured database.
*
* @return The platform
*/
protected Platform getPlatform()
{
return _platformConf.getPlatform();
}
/**
* Reads the database model on which the commands will work.
*
* @return The database model
*/
protected abstract Database readModel();
/**
* Initializes the logging.
*/
private void initLogging()
{
if (_simpleLogging)
{
// For Ant, we're forcing DdlUtils to do logging via log4j to the console
Properties props = new Properties();
String level = (_verbosity == null ? Level.INFO.toString() : _verbosity.getValue()).toUpperCase();
props.setProperty("log4j.rootCategory", level + ",A");
props.setProperty("log4j.appender.A", "org.apache.log4j.ConsoleAppender");
props.setProperty("log4j.appender.A.layout", "org.apache.log4j.PatternLayout");
props.setProperty("log4j.appender.A.layout.ConversionPattern", "%m%n");
// we don't want debug logging from Digester
props.setProperty("log4j.logger.org.apache.commons", "WARN");
LogManager.resetConfiguration();
PropertyConfigurator.configure(props);
}
_log = LogFactory.getLog(getClass());
}
/**
* Executes the commands.
*
* @param model The database model
*/
protected void executeCommands(Database model) throws BuildException
{
for (Iterator it = getCommands(); it.hasNext();)
{
Command cmd = (Command)it.next();
if (cmd.isRequiringModel() && (model == null))
{
throw new BuildException("No database model specified");
}
if (cmd instanceof DatabaseCommand)
{
((DatabaseCommand)cmd).setPlatformConfiguration(_platformConf);
}
cmd.execute(this, model);
}
}
/**
* {@inheritDoc}
*/
public void execute() throws BuildException
{
initLogging();
if (!hasCommands())
{
_log.info("No sub tasks specified, so there is nothing to do.");
return;
}
ClassLoader sysClassLoader = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
try
{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
AntClassLoader newClassLoader = new AntClassLoader(getClass().getClassLoader(), true);
// we're changing the thread classloader so that we can access resources
// from the classpath used to load this task's class
Thread.currentThread().setContextClassLoader(newClassLoader);
return contextClassLoader;
}
catch (SecurityException ex)
{
throw new BuildException("Could not change the context clas loader", ex);
}
}
});
try
{
executeCommands(readModel());
}
finally
{
if ((getDataSource() != null) && isShutdownDatabase())
{
getPlatform().shutdownDatabase();
}
// rollback of our classloader change
Thread.currentThread().setContextClassLoader(sysClassLoader);
}
}
}