View Javadoc
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 }