1 package edu.internet2.middleware.grouper.ext.org.apache.ddlutils.task; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import java.util.ArrayList; 23 import java.util.Iterator; 24 import java.util.Properties; 25 26 import org.apache.commons.dbcp.BasicDataSource; 27 import org.apache.commons.logging.Log; 28 import org.apache.commons.logging.LogFactory; 29 import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.Platform; 30 import edu.internet2.middleware.grouper.ext.org.apache.ddlutils.model.Database; 31 import org.apache.log4j.Level; 32 import org.apache.log4j.LogManager; 33 import org.apache.log4j.PropertyConfigurator; 34 import org.apache.tools.ant.AntClassLoader; 35 import org.apache.tools.ant.BuildException; 36 import org.apache.tools.ant.Task; 37 38 /** 39 * Base class for DdlUtils Ant tasks that operate on a database. 40 * 41 * @version $Revision: 289996 $ 42 * @ant.task ignore="true" 43 */ 44 public abstract class DatabaseTaskBase extends Task 45 { 46 /** The log. */ 47 protected Log _log; 48 49 /** The platform configuration. */ 50 private PlatformConfigurationt/org/apache/ddlutils/task/PlatformConfiguration.html#PlatformConfiguration">PlatformConfiguration _platformConf = new PlatformConfiguration(); 51 /** The sub tasks to execute. */ 52 private ArrayList _commands = new ArrayList(); 53 /** Whether to use simple logging (that the Ant task configures itself via the {@link #_verbosity} setting. */ 54 private boolean _simpleLogging = true; 55 /** The verbosity of the task's debug output. */ 56 private VerbosityLevel _verbosity = null; 57 58 /** 59 * Specifies whether simple logging (configured by the task via the <code>verbosity</code> 60 * setting) shall be used, or whether logging is configured outside of the task 61 * (e.g. via a log4j properties file). 62 * 63 * @param simpleLogging Whether to use simple logging or not 64 * @ant.not-required Per default, simple logging is enabled. 65 */ 66 public void setSimpleLogging(boolean simpleLogging) 67 { 68 _simpleLogging = simpleLogging; 69 } 70 71 /** 72 * Specifies the verbosity of the task's debug output. 73 * 74 * @param level The verbosity level 75 * @ant.not-required Default is <code>INFO</code>. 76 */ 77 public void setVerbosity(VerbosityLevel level) 78 { 79 _verbosity = level; 80 } 81 82 /** 83 * Returns the database type. 84 * 85 * @return The database type 86 */ 87 public String getDatabaseType() 88 { 89 return _platformConf.getDatabaseType(); 90 } 91 92 /** 93 * Specifies the database type. You should only need to set this if DdlUtils is not able to 94 * derive the setting from the name of the used jdbc driver or the jdbc connection url. 95 * If you have to specify this, please post your jdbc driver and connection url combo 96 * to the user mailing list so that DdlUtils can be enhanced to support this combo.<br/> 97 * Valid values are currently:<br/><code>axion, cloudscape, db2, derby, firebird, hsqldb, interbase, 98 * maxdb, mckoi, mssql, mysql, mysql5, oracle, oracle9, oracle10, postgresql, sapdb, sybase</code> 99 * 100 * @param type The database type 101 * @ant.not-required Per default, DdlUtils tries to determine the database type via JDBC. 102 */ 103 public void setDatabaseType(String type) 104 { 105 if ((type != null) && (type.length() > 0)) 106 { 107 _platformConf.setDatabaseType(type); 108 } 109 } 110 111 /** 112 * Returns the data source. 113 * 114 * @return The data source 115 */ 116 public BasicDataSource getDataSource() 117 { 118 return _platformConf.getDataSource(); 119 } 120 121 /** 122 * Adds the data source to use for accessing the database. 123 * 124 * @param dataSource The data source 125 */ 126 public void addConfiguredDatabase(BasicDataSource dataSource) 127 { 128 _platformConf.setDataSource(dataSource); 129 } 130 131 /** 132 * Specifies a pattern that defines which database catalogs to use. For some 133 * more info on catalog patterns and JDBC, see 134 * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html">java.sql.DatabaseMetaData</a>. 135 * 136 * @param catalogPattern The catalog pattern 137 * @ant.not-required Per default no specific catalog is used. 138 */ 139 public void setCatalogPattern(String catalogPattern) 140 { 141 if ((catalogPattern != null) && (catalogPattern.length() > 0)) 142 { 143 _platformConf.setCatalogPattern(catalogPattern); 144 } 145 } 146 147 /** 148 * Specifies a pattern that defines which database schemas to use. For some 149 * more info on schema patterns and JDBC, see 150 * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html">java.sql.DatabaseMetaData</a>. 151 * 152 * @param schemaPattern The schema pattern 153 * @ant.not-required Per default no specific schema is used. 154 */ 155 public void setSchemaPattern(String schemaPattern) 156 { 157 if ((schemaPattern != null) && (schemaPattern.length() > 0)) 158 { 159 _platformConf.setSchemaPattern(schemaPattern); 160 } 161 } 162 163 /** 164 * Determines whether delimited SQL identifiers shall be used (the default). 165 * 166 * @return <code>true</code> if delimited SQL identifiers shall be used 167 */ 168 public boolean isUseDelimitedSqlIdentifiers() 169 { 170 return _platformConf.isUseDelimitedSqlIdentifiers(); 171 } 172 173 /** 174 * Specifies whether DdlUtils shall use delimited (quoted) identifiers (such as table and column 175 * names). Most databases convert undelimited identifiers to uppercase and ignore the case of 176 * identifiers when performing any SQL command. Undelimited identifiers also cannot be reserved 177 * words and can only contain alphanumerical characters and the underscore.<br/> 178 * These limitations do not exist for delimited identifiers where identifiers have to be enclosed 179 * in double quotes. Delimited identifiers can contain unicode characters, and even reserved 180 * words can be used as identifiers. Please be aware though, that they always have to enclosed 181 * in double quotes, and that the case of the identifier will be important in every SQL command 182 * executed against the database. 183 * 184 * @param useDelimitedSqlIdentifiers <code>true</code> if delimited SQL identifiers shall be used 185 * @ant.not-required Default is <code>false</code>. 186 */ 187 public void setUseDelimitedSqlIdentifiers(boolean useDelimitedSqlIdentifiers) 188 { 189 _platformConf.setUseDelimitedSqlIdentifiers(useDelimitedSqlIdentifiers); 190 } 191 192 /** 193 * Determines whether a table's foreign keys read from a live database 194 * shall be sorted alphabetically. Is <code>false</code> by default. 195 * 196 * @return <code>true</code> if the foreign keys shall be sorted 197 */ 198 public boolean isSortForeignKeys() 199 { 200 return _platformConf.isSortForeignKeys(); 201 } 202 203 /** 204 * Specifies whether DdlUtils shall sort the foreign keys of a table read from a live database or 205 * leave them in the order in which they are returned by the database/JDBC driver. Note that 206 * the sort is case sensitive only if delimited identifier mode is on 207 * (<code>useDelimitedSqlIdentifiers</code> is set to <code>true</code>). 208 * 209 * @param sortForeignKeys <code>true</code> if the foreign keys shall be sorted 210 * @ant.not-required Default is <code>false</code>. 211 */ 212 public void setSortForeignKeys(boolean sortForeignKeys) 213 { 214 _platformConf.setSortForeignKeys(sortForeignKeys); 215 } 216 217 /** 218 * Determines whether the database shall be shut down after the task has finished. 219 * 220 * @return <code>true</code> if the database shall be shut down 221 */ 222 public boolean isShutdownDatabase() 223 { 224 return _platformConf.isShutdownDatabase(); 225 } 226 227 /** 228 * Specifies whether DdlUtils shall shut down the database after the task has finished. 229 * This is mostly useful for embedded databases. 230 * 231 * @param shutdownDatabase <code>true</code> if the database shall be shut down 232 * @ant.not-required Default is <code>false</code>. 233 */ 234 public void setShutdownDatabase(boolean shutdownDatabase) 235 { 236 _platformConf.setShutdownDatabase(shutdownDatabase); 237 } 238 239 /** 240 * Adds a command. 241 * 242 * @param command The command 243 */ 244 protected void addCommand(Command command) 245 { 246 _commands.add(command); 247 } 248 249 /** 250 * Determines whether there are commands to perform. 251 * 252 * @return <code>true</code> if there are commands 253 */ 254 protected boolean hasCommands() 255 { 256 return !_commands.isEmpty(); 257 } 258 259 /** 260 * Returns the commands. 261 * 262 * @return The commands 263 */ 264 protected Iterator getCommands() 265 { 266 return _commands.iterator(); 267 } 268 269 /** 270 * Creates the platform configuration. 271 * 272 * @return The platform configuration 273 */ 274 protected PlatformConfiguration getPlatformConfiguration() 275 { 276 return _platformConf; 277 } 278 279 /** 280 * Creates the platform for the configured database. 281 * 282 * @return The platform 283 */ 284 protected Platform getPlatform() 285 { 286 return _platformConf.getPlatform(); 287 } 288 289 /** 290 * Reads the database model on which the commands will work. 291 * 292 * @return The database model 293 */ 294 protected abstract Database readModel(); 295 296 /** 297 * Initializes the logging. 298 */ 299 private void initLogging() 300 { 301 // For Ant, we're forcing DdlUtils to do logging via log4j to the console 302 Properties props = new Properties(); 303 String level = (_verbosity == null ? Level.INFO.toString() : _verbosity.getValue()).toUpperCase(); 304 305 props.setProperty("log4j.rootCategory", level + ",A"); 306 props.setProperty("log4j.appender.A", "org.apache.log4j.ConsoleAppender"); 307 props.setProperty("log4j.appender.A.layout", "org.apache.log4j.PatternLayout"); 308 props.setProperty("log4j.appender.A.layout.ConversionPattern", "%m%n"); 309 // we don't want debug logging from Digester/Betwixt 310 props.setProperty("log4j.logger.org.apache.commons", "WARN"); 311 312 LogManager.resetConfiguration(); 313 PropertyConfigurator.configure(props); 314 315 _log = LogFactory.getLog(getClass()); 316 } 317 318 /** 319 * Executes the commands. 320 * 321 * @param model The database model 322 */ 323 protected void executeCommands(Database model) throws BuildException 324 { 325 for (Iterator it = getCommands(); it.hasNext();) 326 { 327 Command="../../../../../../../../../edu/internet2/middleware/grouper/ext/org/apache/ddlutils/task/Command.html#Command">Command cmd = (Command)it.next(); 328 329 if (cmd.isRequiringModel() && (model == null)) 330 { 331 throw new BuildException("No database model specified"); 332 } 333 if (cmd instanceof DatabaseCommand) 334 { 335 ((DatabaseCommand)cmd).setPlatformConfiguration(_platformConf); 336 } 337 cmd.execute(this, model); 338 } 339 } 340 341 /** 342 * {@inheritDoc} 343 */ 344 public void execute() throws BuildException 345 { 346 if (_simpleLogging) { 347 initLogging(); 348 } 349 350 if (!hasCommands()) 351 { 352 _log.info("No sub tasks specified, so there is nothing to do."); 353 return; 354 } 355 356 ClassLoader sysClassLoader = Thread.currentThread().getContextClassLoader(); 357 AntClassLoader newClassLoader = new AntClassLoader(getClass().getClassLoader(), true); 358 359 // we're changing the thread classloader so that we can access resources 360 // from the classpath used to load this task's class 361 Thread.currentThread().setContextClassLoader(newClassLoader); 362 363 try 364 { 365 executeCommands(readModel()); 366 } 367 finally 368 { 369 if ((getDataSource() != null) && isShutdownDatabase()) 370 { 371 getPlatform().shutdownDatabase(); 372 } 373 // rollback of our classloader change 374 Thread.currentThread().setContextClassLoader(sysClassLoader); 375 } 376 } 377 }