View Javadoc

1   package org.apache.torque.task;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
5    * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
7    * License. You may obtain a copy of the License at
8    * 
9    * http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
12   * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
13   * specific language governing permissions and limitations under the License.
14   */
15  
16  import java.io.File;
17  import java.util.ArrayList;
18  import java.util.Date;
19  import java.util.Hashtable;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.texen.ant.TexenTask;
26  import org.apache.tools.ant.BuildException;
27  import org.apache.tools.ant.DirectoryScanner;
28  import org.apache.tools.ant.types.FileSet;
29  import org.apache.torque.engine.EngineException;
30  import org.apache.torque.engine.database.model.Database;
31  import org.apache.torque.engine.database.model.Table;
32  import org.apache.velocity.VelocityContext;
33  import org.apache.velocity.context.Context;
34  import org.kuali.core.db.torque.DatabaseParser;
35  import org.kuali.core.db.torque.KualiXmlToAppData;
36  import org.kuali.core.db.torque.StringFilter;
37  import org.kuali.core.db.torque.pojo.KualiDatabase;
38  import org.kuali.core.db.torque.pojo.Sequence;
39  import org.kuali.core.db.torque.pojo.View;
40  
41  /**
42   * A base torque task that uses either a single XML schema representing a data model, or a <fileset> of XML
43   * schemas. We are making the assumption that an XML schema representing a data model contains tables for a
44   * <strong>single</strong> database.
45   * 
46   * @author <a href="mailto:jvanzyl@zenplex.com">Jason van Zyl</a>
47   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
48   */
49  public class TorqueDataModelTask extends TexenTask {
50      List<String> tblIncludes;
51      List<String> tblExcludes;
52      List<String> vIncludes;
53      List<String> sIncludes;
54      List<String> vExcludes;
55      List<String> sExcludes;
56  
57      /**
58       * XML that describes the database model, this is transformed into the application model object.
59       */
60      protected String xmlFile;
61  
62      /** Fileset of XML schemas which represent our data models. */
63      protected List<FileSet> filesets = new ArrayList<FileSet>();
64  
65      /** Data models that we collect. One from each XML schema file. */
66      protected List<Database> dataModels = new ArrayList<Database>();
67  
68      /** Velocity context which exposes our objects in the templates. */
69      protected Context context;
70  
71      /**
72       * Map of data model name to database name. Should probably stick to the convention of them being the same but I
73       * know right now in a lot of cases they won't be.
74       */
75      protected Hashtable<String, String> dataModelDbMap;
76  
77      /**
78       * Hashtable containing the names of all the databases in our collection of schemas.
79       */
80      protected Hashtable<String, String> databaseNames;
81  
82      // !! This is probably a crappy idea having the sql file -> db map
83      // here. I can't remember why I put it here at the moment ...
84      // maybe I was going to map something else. It can probably
85      // move into the SQL task.
86  
87      /**
88       * Name of the properties file that maps an SQL file to a particular database.
89       */
90      protected String sqldbmap;
91  
92      /** The target database(s) we are generating SQL for. */
93      private String targetDatabase;
94  
95      /** Target Java package to place the generated files in. */
96      private String targetPackage;
97  
98      /**
99       * Set the sqldbmap.
100      * 
101      * @param sqldbmap
102      *            th db map
103      */
104     public void setSqlDbMap(String sqldbmap) {
105         // !! Make all these references files not strings.
106         this.sqldbmap = getProject().resolveFile(sqldbmap).toString();
107     }
108 
109     /**
110      * Get the sqldbmap.
111      * 
112      * @return String sqldbmap.
113      */
114     public String getSqlDbMap() {
115         return sqldbmap;
116     }
117 
118     /**
119      * Return the data models that have been processed.
120      * 
121      * @return List data models
122      */
123     public List<Database> getDataModels() {
124         return dataModels;
125     }
126 
127     /**
128      * Return the data model to database name map.
129      * 
130      * @return Hashtable data model name to database name map.
131      */
132     public Hashtable<String, String> getDataModelDbMap() {
133         return dataModelDbMap;
134     }
135 
136     /**
137      * Get the xml schema describing the application model.
138      * 
139      * @return String xml schema file.
140      */
141     public String getXmlFile() {
142         return xmlFile;
143     }
144 
145     /**
146      * Set the xml schema describing the application model.
147      * 
148      * @param xmlFile
149      *            The new XmlFile value
150      */
151     public void setXmlFile(String xmlFile) {
152         this.xmlFile = xmlFile;
153     }
154 
155     /**
156      * Adds a set of xml schema files (nested fileset attribute).
157      * 
158      * @param set
159      *            a Set of xml schema files
160      */
161     public void addFileset(FileSet set) {
162         filesets.add(set);
163     }
164 
165     /**
166      * Get the current target database.
167      * 
168      * @return String target database(s)
169      */
170     public String getTargetDatabase() {
171         return targetDatabase;
172     }
173 
174     /**
175      * Set the current target database. (e.g. mysql, oracle, ..)
176      */
177     public void setTargetDatabase(String targetDatabase) {
178         this.targetDatabase = targetDatabase;
179     }
180 
181     /**
182      * Get the current target package.
183      * 
184      * @return return target java package.
185      */
186     public String getTargetPackage() {
187         return targetPackage;
188     }
189 
190     /**
191      * Set the current target package. This is where generated java classes will live.
192      */
193     public void setTargetPackage(String targetPackage) {
194         this.targetPackage = targetPackage;
195     }
196 
197     /**
198      * Return a SAX parser that implements the DatabaseParser interface
199      */
200     protected DatabaseParser getDatabaseParser() {
201         return new KualiXmlToAppData(getTargetDatabase(), getTargetPackage());
202     }
203 
204     /**
205      * Parse a schema XML File into a Database object
206      */
207     protected Database getDataModel(File file) throws EngineException {
208         // Get a handle to a parser
209         DatabaseParser databaseParser = getDatabaseParser();
210 
211         // Parse the file into a database
212         Database database = databaseParser.parseResource(file.toString());
213 
214         KualiDatabase kdb = (KualiDatabase) database;
215 
216         // Filter out tables as needed
217         filterTables(database);
218 
219         // Filter out views as needed
220         filterViews(kdb);
221 
222         // Filter out sequences as needed
223         filterSequences(kdb);
224 
225         // Extract the filename
226         database.setFileName(grokName(file.toString()));
227 
228         // return the database
229         return database;
230     }
231 
232     protected List<Table> getTables(Database database) {
233         List<Table> tables = new ArrayList<Table>();
234         for (Object object : database.getTables()) {
235             Table table = (Table) object;
236             tables.add(table);
237         }
238         return tables;
239     }
240 
241     protected void filterSequences(KualiDatabase database) {
242         StringFilter filter = new StringFilter(sIncludes, sExcludes);
243         Iterator<Sequence> itr = database.getSequences().iterator();
244         while (itr.hasNext()) {
245             Sequence sequence = itr.next();
246             String name = sequence.getName();
247             boolean remove = !filter.isInclude(name) || filter.isExclude(name);
248             if (remove) {
249                 System.out.println("[INFO] Filtering out sequence " + name);
250                 itr.remove();
251             }
252         }
253     }
254 
255     protected void filterViews(KualiDatabase database) {
256         StringFilter filter = new StringFilter(vIncludes, vExcludes);
257         Iterator<View> itr = database.getViews().iterator();
258         while (itr.hasNext()) {
259             View view = itr.next();
260             String name = view.getName();
261             boolean remove = !filter.isInclude(name) || filter.isExclude(name);
262             if (remove) {
263                 System.out.println("[INFO] Filtering out view " + name);
264                 itr.remove();
265             }
266         }
267     }
268 
269     protected void filterTables(Database database) {
270         StringFilter filter = new StringFilter(tblIncludes, tblExcludes);
271         List<Table> tables = getTables(database);
272         for (Table table : tables) {
273             String name = table.getName();
274             boolean remove = !filter.isInclude(name) || filter.isExclude(name);
275             if (remove) {
276                 System.out.println("[INFO] Filtering out table " + name);
277                 database.removeTable(table);
278             }
279         }
280     }
281 
282     /**
283      * Get the list of schema XML files from our filesets
284      */
285     protected List<File> getDataModelFiles() {
286         // Allocate some storage
287         List<File> dataModelFiles = new ArrayList<File>();
288 
289         // Iterate through the filesets
290         for (int i = 0; i < getFilesets().size(); i++) {
291             // Extract a fileset
292             FileSet fs = getFilesets().get(i);
293 
294             // Create a directory scanner
295             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
296 
297             // Figure out the directory to scan
298             File srcDir = fs.getDir(getProject());
299 
300             // Scan the directory
301             String[] dataModelFilesArray = ds.getIncludedFiles();
302 
303             // Add each file in the directory to our list
304             for (int j = 0; j < dataModelFilesArray.length; j++) {
305                 File file = new File(srcDir, dataModelFilesArray[j]);
306                 dataModelFiles.add(file);
307             }
308         }
309 
310         // Return the list of schema.xml files
311         return dataModelFiles;
312     }
313 
314     /**
315      * Parse schema XML files into Database objects
316      */
317     protected List<Database> getPopulatedDataModels() throws EngineException {
318         // Allocate some storage
319         List<Database> databases = new ArrayList<Database>();
320 
321         // Only one file to parse
322         if (getXmlFile() != null) {
323             // Parse the file into a database object
324             Database database = getDataModel(new File(getXmlFile()));
325             // Add it to our list
326             databases.add(database);
327             // we are done
328             return databases;
329         }
330 
331         // Get the list of schema XML files to parse from our filesets
332         List<File> dataModelFiles = getDataModelFiles();
333         // Iterate through the list, parsing each schema.xml file into a database object
334         for (File dataModelFile : dataModelFiles) {
335             // Parse a schema.xml file into a database object
336             Database database = getDataModel(dataModelFile);
337             // Add the database object to our list
338             databases.add(database);
339         }
340         // Return the list of database objects
341         return databases;
342     }
343 
344     /**
345      * Set up the initial context for generating SQL
346      * 
347      * @return the context
348      * @throws Exception
349      */
350     @Override
351     public Context initControlContext() throws Exception {
352         if (xmlFile == null && filesets.isEmpty()) {
353             throw new BuildException("You must specify an XML schema or fileset of XML schemas!");
354         }
355 
356         try {
357             dataModels = getPopulatedDataModels();
358         } catch (EngineException ee) {
359             throw new BuildException(ee);
360         }
361 
362         Iterator<Database> i = dataModels.iterator();
363         databaseNames = new Hashtable<String, String>();
364         dataModelDbMap = new Hashtable<String, String>();
365 
366         // Different datamodels may state the same database
367         // names, we just want the unique names of databases.
368         while (i.hasNext()) {
369             Database database = i.next();
370             databaseNames.put(database.getName(), database.getName());
371             dataModelDbMap.put(database.getFileName(), database.getName());
372         }
373 
374         context = new VelocityContext();
375 
376         // Place our set of data models into the context along
377         // with the names of the databases as a convenience for now.
378         context.put("dataModels", dataModels);
379         context.put("databaseNames", databaseNames);
380         context.put("targetDatabase", targetDatabase);
381         context.put("targetPackage", targetPackage);
382 
383         return context;
384     }
385 
386     /**
387      * Change type of "now" to java.util.Date
388      * 
389      * @see org.apache.velocity.texen.ant.TexenTask#populateInitialContext(org.apache.velocity.context.Context)
390      */
391     @Override
392     protected void populateInitialContext(Context context) throws Exception {
393         super.populateInitialContext(context);
394         context.put("now", new Date());
395     }
396 
397     /**
398      * Gets a name to use for the application's data model.
399      * 
400      * @param xmlFile
401      *            The path to the XML file housing the data model.
402      * @return The name to use for the <code>AppData</code>.
403      */
404     private String grokName(String xmlFile) {
405         // This can't be set from the file name as it is an unreliable
406         // method of naming the descriptor. Not everyone uses the same
407         // method as I do in the TDK. jvz.
408 
409         String name = "data-model";
410         int i = xmlFile.lastIndexOf(System.getProperty("file.separator"));
411         if (i != -1) {
412             // Creep forward to the start of the file name.
413             i++;
414 
415             int j = xmlFile.lastIndexOf('.');
416             if (i < j) {
417                 name = xmlFile.substring(i, j);
418             } else {
419                 // Weirdo
420                 name = xmlFile.substring(i);
421             }
422         }
423         return name;
424     }
425 
426     /**
427      * Override Texen's context properties to map the torque.xxx properties (including defaults set by the
428      * org/apache/torque/defaults.properties) to just xxx.
429      * 
430      * <p>
431      * Also, move xxx.yyy properties to xxxYyy as Velocity doesn't like the xxx.yyy syntax.
432      * </p>
433      * 
434      * @param file
435      *            the file to read the properties from
436      */
437     @Override
438     public void setContextProperties(String file) {
439         super.setContextProperties(file);
440 
441         // Map the torque.xxx elements from the env to the contextProperties
442         Hashtable<?, ?> env = super.getProject().getProperties();
443         for (Iterator<?> i = env.entrySet().iterator(); i.hasNext();) {
444             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
445             String key = (String) entry.getKey();
446             if (key.startsWith("torque.")) {
447                 String newKey = key.substring("torque.".length());
448                 int j = newKey.indexOf(".");
449                 while (j != -1) {
450                     newKey = newKey.substring(0, j) + StringUtils.capitalize(newKey.substring(j + 1));
451                     j = newKey.indexOf(".");
452                 }
453 
454                 contextProperties.setProperty(newKey, entry.getValue());
455             }
456         }
457     }
458 
459     public List<FileSet> getFilesets() {
460         return filesets;
461     }
462 
463     public void setFilesets(List<FileSet> filesets) {
464         this.filesets = filesets;
465     }
466 
467     public List<String> getTblIncludes() {
468         return tblIncludes;
469     }
470 
471     public void setTblIncludes(List<String> includes) {
472         this.tblIncludes = includes;
473     }
474 
475     public List<String> getTblExcludes() {
476         return tblExcludes;
477     }
478 
479     public void setTblExcludes(List<String> excludes) {
480         this.tblExcludes = excludes;
481     }
482 
483     public List<String> getvIncludes() {
484         return vIncludes;
485     }
486 
487     public void setvIncludes(List<String> vIncludes) {
488         this.vIncludes = vIncludes;
489     }
490 
491     public List<String> getsIncludes() {
492         return sIncludes;
493     }
494 
495     public void setsIncludes(List<String> sIncludes) {
496         this.sIncludes = sIncludes;
497     }
498 
499     public List<String> getvExcludes() {
500         return vExcludes;
501     }
502 
503     public void setvExcludes(List<String> vExcludes) {
504         this.vExcludes = vExcludes;
505     }
506 
507     public List<String> getsExcludes() {
508         return sExcludes;
509     }
510 
511     public void setsExcludes(List<String> sExcludes) {
512         this.sExcludes = sExcludes;
513     }
514 }