View Javadoc

1   package org.devaki.nextobjects.util;
2   /*
3    *  nextobjects Copyright (C) 2001-2005 Emmanuel Florent
4    *  This program is free software; you can redistribute it and/or modify
5    *  it under the terms of the GNU General Public License as published by the
6    *  Free Software Foundation; either version 2 of the License, or (at your
7    *  option) any later version.
8    *  This program is distributed in the hope that it will
9    *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
10   *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11   *  PURPOSE. See the GNU General Public License for more details.
12   *  You should have received a copy of the GNU General Public License along
13   *  with this program; if not, write to the Free Software Foundation, Inc., 59
14   *  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15   */
16  import java.awt.Cursor;
17  import java.io.File;
18  import java.io.FileOutputStream;
19  import java.io.FileInputStream;
20  import java.io.IOException;
21  import java.util.Map;
22  import java.io.PrintStream;
23  import java.io.PrintWriter;
24  import java.net.URL;
25  import java.util.Iterator;
26  import java.util.Properties;
27  import javax.xml.transform.Result;
28  import javax.xml.transform.Templates;
29  import javax.xml.transform.TransformerConfigurationException;
30  import javax.xml.transform.TransformerException;
31  import javax.xml.transform.TransformerFactory;
32  import javax.xml.transform.sax.SAXResult;
33  import javax.xml.transform.sax.SAXSource;
34  import javax.xml.transform.sax.SAXTransformerFactory;
35  import javax.xml.transform.sax.TemplatesHandler;
36  import javax.xml.transform.sax.TransformerHandler;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.tools.ant.BuildException;
40  import org.apache.tools.ant.Project;
41  import org.apache.tools.ant.ProjectHelper;
42  import org.apache.xml.serializer.OutputPropertiesFactory;
43  import org.apache.xml.serializer.Serializer;
44  import org.apache.xml.serializer.SerializerFactory;
45  import org.devaki.nextobjects.NextObjects;
46  import org.devaki.nextobjects.ui.editor.SqlEditor;
47  import org.devaki.nextobjects.ui.main.NOLog;
48  import org.devaki.nextobjects.workspace.models.Database;
49  import org.devaki.nextobjects.workspace.models.PhysicalModel;
50  import org.devaki.nextobjects.workspace.models.columns.Column;
51  import org.devaki.nextobjects.workspace.models.columns.ColumnType;
52  import org.devaki.nextobjects.workspace.models.graphics.LineView;
53  import org.devaki.nextobjects.workspace.models.objects.BaseClass;
54  import org.devaki.nextobjects.workspace.models.objects.Constraint;
55  import org.devaki.nextobjects.workspace.models.objects.InheritanceLink;
56  import org.devaki.nextobjects.workspace.models.objects.Table;
57  import org.xml.sax.ErrorHandler;
58  import org.xml.sax.InputSource;
59  import org.xml.sax.SAXException;
60  import org.xml.sax.XMLReader;
61  import org.xml.sax.helpers.XMLReaderFactory;
62  /***
63   *  This class is responsible for starting torque-generator It also write
64   *  '${project}-schema.xml' it also write torque's project.properties additional
65   *  build.properties might be passed to torque-gen throught
66   *  APP_HOME/defaultbuild.properties
67   *
68   * @author    <a href="mailto:eflorent@devaki.org">Emmanuel Florent</a>
69   */
70  public class TorqueWrapper
71  {
72      /***  Description of the Field */
73      private static boolean pursue = true;
74      /***  Torque's projects-schema file */
75      private static File buildFile =
76          new File(
77              NOPreferences.getProperty("nextobjects.io.torque.home")
78                  + File.separator
79                  + "build-torque.xml");
80      /***  log4j logging system */
81      private static Log logger = LogFactory.getLog(ModelMan.class.getName());
82      /***  Constructor do nothing except avoid instanciation  */
83      public TorqueWrapper()
84      {
85      }
86      /***
87       *  Description of the Method
88       *
89       * @param  pDatabase  Description of the Parameter
90       */
91      private void prepareBuild(final PhysicalModel pDatabase)
92      {
93          this.clean();
94          this.inheritColumns(pDatabase);
95          this.makeBuildProperties(pDatabase);
96          this.writeIdTableSchema(pDatabase);
97          try
98          {
99              this.writeXML(pDatabase);
100         }
101         catch (TransformerConfigurationException e)
102         {
103             logger.error(e.getMessage());
104             e.printStackTrace();
105         }
106         catch (TransformerException e)
107         {
108             logger.error(e.getMessage());
109             e.printStackTrace();
110         }
111         catch (SAXException e)
112         {
113             logger.error(e.getMessage());
114         }
115         catch (IOException e)
116         {
117             logger.error(e.getMessage());
118             e.printStackTrace();
119         }
120     }
121     /***
122      *  Generate Object-Model
123      *
124      * @param  db  the database
125      */
126     public void genObjectModel(final PhysicalModel db)
127     {
128         prepareBuild(db);
129         runTask(db, "om");
130     }
131     /***
132      *  Generate DTD
133      *
134      * @param  db  the database
135      */
136     public void genDTD(final PhysicalModel db)
137     {
138         prepareBuild(db);
139         runTask(db, "datadtd");
140     }
141     /***
142      *  Generate XML from JDBC
143      *
144      * @param  db  the database
145      */
146     public  void jdbc2xml(final PhysicalModel db)
147     {
148         makeBuildProperties(db);
149         NextObjects.getReference().setCursor(new Cursor(Cursor.WAIT_CURSOR));
150         ClassLoader coreLoader = TorqueWrapper.class.getClassLoader();
151         try
152         {
153             runBuild(coreLoader, "jdbc");
154         }
155         catch (BuildException be)
156         {
157             logger.error(be.getMessage());
158         }
159         // Reset cursor
160         NextObjects.getReference().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
161         // we have nothing to return (swingworker)
162     }
163     /***
164      *  Run create-db task
165      *
166      * @param  db  the database
167      */
168     public  void createDB(final PhysicalModel db)
169     {
170         makeBuildProperties(db);
171         runTask(db, "create-db");
172     }
173     /***
174      *  Run insert-sql task
175      *
176      * @param  db  the database
177      */
178     public  void insertSQL(final PhysicalModel db)
179     {
180         makeBuildProperties(db);
181         runTask(db, "insert-sql");
182     }
183     /***
184      *  Inherits all columns move all the columns of a table to it's immediate
185      *  parent
186      *
187      * @param  model  Description of the Parameter
188      * @see           http://db.apache.org/torque/inheritance-guide.html
189      */
190     public  void inheritColumns(final PhysicalModel model)
191     {
192         Iterator itInhrLnk = model.getInheritanceLinks().iterator();
193         while (itInhrLnk.hasNext())
194         {
195             inheritColumn(model, (InheritanceLink) itInhrLnk.next());
196         }
197         model.getPanel().repaint();
198     }
199     /***
200      *  Inherits all columns move all the columns of a table to it's immediate
201      *  parent
202      *
203      * @param  tmpLink  the inheritance link
204      * @param  pModel   Description of the Parameter
205      * @see             http://db.apache.org/torque/inheritance-guide.html
206      */
207     private  void inheritColumn(
208         final PhysicalModel pModel,
209         final InheritanceLink tmpLink)
210     {
211         if (!((Table) tmpLink.getChildClass()).getSkipSql())
212         {
213             Column tmpColumn = null;
214             // moving tmpLink.getChildClass() to tmpLink.getParentClass()
215             for (int i = 0; i < tmpLink.getChildClass().getData().size(); i++)
216             {
217                 // moved columns
218                 tmpColumn =
219                     new Column(
220                         (Column) tmpLink.getChildClass().getData().elementAt(
221                             i));
222                 tmpLink.getParentClass().getData().addElement(tmpColumn);
223             }
224             tmpColumn = null;
225             if (((BaseClass) tmpLink.getParentClass()).getColumnForId("class")
226                 == null)
227             {
228                 tmpColumn = new Column("class",                    //pName,
229     "class",                    //pCode
230     new ColumnType("INTEGER", null, ""),                    //pType
231     "",                    //pSize,
232     "",                    //pDefaultValue,
233     false,                    //pPrimaryKey,
234     true,                    //pRequired,
235     false,                    //pAutoIncrement,
236     false,                    // unique
237     false,                    //index
238     "",                    //pJavaName,
239     "",                    //pJavaType,
240     "single",                    //pInheritance,
241     "",                    //pInputValidator,
242     "",                    //pJavaNamingMethod,
243     "",                    //pDescription,
244      (BaseClass) tmpLink.getParentClass()                    // pBaseClass
245     );
246                 tmpLink.getParentClass().getData().addElement(tmpColumn);
247             }
248             ((Table) tmpLink.getChildClass()).setSkipSql(true);
249             tmpColumn =
250                 ((Table) tmpLink.getParentClass()).getColumnForId("class");
251             tmpColumn.getInheritanceLinks().addElement(tmpLink);
252             Iterator itCst = pModel.getConstraints().iterator();
253             while (itCst.hasNext())
254             {
255                 Constraint cst = (Constraint) itCst.next();
256                 if (cst.getParentClass() == tmpLink.getChildClass())
257                 {
258                     cst.setParentClass(tmpLink.getParentClass());
259                     ((LineView) cst.getObjectView()).computeBestPoints();
260                 }
261                 if (cst.getChildClass() == tmpLink.getChildClass())
262                 {
263                     cst.setChildClass(tmpLink.getParentClass());
264                     ((LineView) cst.getObjectView()).computeBestPoints();
265                 }
266             }
267         }
268     }
269     /***
270      *  Generate Documentation
271      *
272      * @param  pDatabase  the database
273      */
274     public void genDoc(final PhysicalModel pDatabase)
275     {
276         prepareBuild(pDatabase);
277         runTask(pDatabase, "doc");
278         runTask(pDatabase, "java-doc");
279     }
280     /***
281      *  Generate OJB
282      *
283      * @param  pDatabase  the database
284      */
285     public void genOjb(final PhysicalModel pDatabase)
286     {
287         prepareBuild(pDatabase);
288         runTask(pDatabase, "ojb-repository");
289         runTask(pDatabase, "ojb-model");
290     }
291     /***
292      *  Generate SQL for the current model
293      *
294      * @param  pDatabase  the database
295      */
296     public  void genSQL(final PhysicalModel pDatabase)
297     {
298         prepareBuild(pDatabase);
299         runTask(pDatabase, "sql");
300     }
301     /***
302      *  Write the torque's id-table-schema
303      *
304      * @param  pDatabase  Description of the Parameter
305      */
306     public  void writeIdTableSchema(final PhysicalModel pDatabase)
307     {
308         String xml =
309             "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" "
310                 + "standalone=\"no\" ?>";
311         xml += "<!DOCTYPE database "
312             + "SYSTEM \"http://db.apache.org/torque/dtd/database_3_1.dtd\">";
313         xml += "<database name=\"" + pDatabase.getId() + "\">";
314         xml += "    <table name=\"ID_TABLE\" idMethod=\"idbroker\">";
315         xml += "        <column name=\"ID_TABLE_ID\" required=\"true\" "
316             + "            primaryKey=\"true\" type=\"INTEGER\"/>";
317         xml += "        <column name=\"TABLE_NAME\" required=\"true\" "
318             + "            size=\"255\" type=\"VARCHAR\"/>";
319         xml += "        <column name=\"NEXT_ID\"  type=\"INTEGER\"/>";
320         xml += "        <column name=\"QUANTITY\" type=\"INTEGER\"/>";
321         xml += "        <unique><unique-column name=\"TABLE_NAME\"/></unique>";
322         xml += "    </table></database>";
323         String filePath =
324             NOPreferences.getProperty("nextobjects.io.torque.home")
325                 + File.separator
326                 + "schema"
327                 + File.separator
328                 + "id-table-schema.xml";
329         // Write the properties
330         try
331         {
332             // write the data out
333             PrintWriter out = new PrintWriter(new FileOutputStream(filePath));
334             out.write(xml);
335             out.flush();
336             out.close();
337             logger.info("Generated " + filePath);
338             pursue = true;
339         }
340         catch (IOException e)
341         {
342             pursue = false;
343             logger.error("error writing " + filePath);
344             logger.error(e.getMessage());
345         }
346     }
347     /***
348      *  Call to ant for the torque project
349      *
350      * @param  pDatabase  the database
351      * @param  pTarget    the torque target
352      */
353     public void runTask(
354         final PhysicalModel pDatabase,
355         final String pTarget)
356     {
357         // Start generating SQL using ant and the argument 'project-sql'
358         //...
359         if (pursue)
360         {
361             SwingWorker worker = new SwingWorker()
362             {
363                 /***
364                  *  Swingworker thread init.
365                  *
366                  * @return    Description of the Return Value
367                  */
368                 public final Object construct()
369                 {
370                     NextObjects.getReference().setCursor(
371                         new Cursor(Cursor.WAIT_CURSOR));
372                     ClassLoader coreLoader =
373                         TorqueWrapper.class.getClassLoader();
374                     try
375                     {
376                         runBuild(coreLoader, pTarget);
377                         logger.info("Done with " + pTarget);
378                         if (pTarget.equals("sql"))
379                         {
380                             String filePath =
381                                 new StringBuffer()
382                                     .append(
383                                         NOPreferences.getProperty(
384                                             "nextobjects.io.torque.home"))
385                                     .append(File.separator)
386                                     .append("src")
387                                     .append(File.separator)
388                                     .append("sql")
389                                     .append(File.separator)
390                                     .append(ModelMan.getCurrentModel().getId())
391                                     .append("-schema.sql")
392                                     .toString();
393                             NOLog.showPane(1);
394                             // show sql
395                             SqlEditor.setFile(new File(filePath));
396                         }
397                     }
398                     catch (BuildException be)
399                     {
400                         logger.error(be.getMessage());
401                     }
402                     // Reset cursor
403                     NextObjects.getReference().setCursor(
404                         new Cursor(Cursor.DEFAULT_CURSOR));
405                     // we have nothing to return (swingworker)
406                     return null;
407                 }
408             };
409             // end of worker
410             worker.start();     
411             worker=null;       
412         }
413         else
414         {
415             logger.error("An error have occured, I give up.");
416         }
417     }
418     /***
419      *  Call to ant for the torque project
420      *
421      * @param  coreLoader  the class loader
422      * @param  pTarget     the torque target
423      */
424     public void runBuild(
425         final ClassLoader coreLoader,
426         final String pTarget)
427     {
428         NOLog.showPane(0);
429         // show log
430         final Project project = new Project();
431         project.setCoreLoader(coreLoader);
432         NOBuildLogger bl = new NOBuildLogger();
433         bl.setOutputPrintStream(new PrintStream(NOLog.getJTextOutStream()));
434         bl.setErrorPrintStream(new PrintStream(NOLog.getJTextOutStream()));
435         project.addBuildListener(bl);
436         project.fireBuildStarted();
437         project.init();
438         project.setUserProperty(
439             "ant.version",
440             org.apache.tools.ant.Main.getAntVersion());
441         project.setUserProperty("ant.file", buildFile.getAbsolutePath());
442         ProjectHelper.configureProject(project, buildFile);
443         project.executeTarget(pTarget);
444     }
445     /***  Make clean. */
446     public  void clean()
447     {
448         String filePath =
449             new StringBuffer()
450                 .append(NOPreferences.getProperty("nextobjects.io.torque.home"))
451                 .append(File.separator)
452                 .append("src")
453                 .append(File.separator)
454                 .toString();
455         logger.info("Cleaning Torque input :" + filePath);
456         File temp = new File(filePath);
457         NOFileUtil.deleteFile(temp);
458         temp.mkdir();
459         filePath =
460             new StringBuffer()
461                 .append(NOPreferences.getProperty("nextobjects.io.torque.home"))
462                 .append(File.separator)
463                 .append("schema")
464                 .append(File.separator)
465                 .toString();
466         temp = new File(filePath);
467         NOFileUtil.deleteFile(temp);
468         temp.mkdir();
469     }
470     /***
471      *  create the file build.properties that is neccessary to run Torque
472      *
473      * @param  pDatabase  Description of the Parameter
474      */
475     public  void makeBuildProperties(final PhysicalModel pDatabase)
476     {
477         Properties build = new Properties();
478         //initialize properties with Object Model infos
479         build.put("torque.home", NOPreferences.getProperty("nextobjects.io.torque.home"));
480         build.put("project", pDatabase.getId());
481         build.put(
482             "database",
483             ((Database) NOPreferences
484                 .TORQUE_DB
485                 .elementAt(pDatabase.getDbType()))
486                 .getCode());
487         build.put("targetPackage", pDatabase.getPackageName());
488         build.put("createDatabaseUrl", pDatabase.getCreateDatabaseUrl());
489         build.put("buildDatabaseUrl", pDatabase.getBuildDatabaseUrl());
490         build.put("databaseUrl", pDatabase.getBuildDatabaseUrl());
491         build.put("torque.database.schema", pDatabase.getSchema());
492         build.put(
493             "databaseDriver",
494             ((Database) NOPreferences
495                 .TORQUE_DB
496                 .elementAt(pDatabase.getDbType()))
497                 .getDriver());
498         build.put("databaseUser", pDatabase.getDatabaseUser());
499         build.put("databasePassword", pDatabase.getDatabasePassword());
500         build.put("databaseHost", pDatabase.getDatabaseHost());
501         build.put("schemaDirectory", "schema");
502         //load various properties from APP_HOME/defaultbuild.properties
503         Properties overwrite = new Properties();
504         File fp =
505             new File(
506                 NOPreferences.APP_HOME
507                     + File.separator
508                     + "defaultbuild.properties");
509         try
510         {
511             FileInputStream inStream = new FileInputStream(fp);
512             overwrite.load(inStream);
513         }
514         catch (IOException e)
515         {
516             logger.error(e.getMessage());
517         }
518         build.putAll((Map) overwrite.clone());
519         // Write the properties
520         try
521         {
522             String s =
523                 NOPreferences.getProperty("nextobjects.io.torque.home")
524                     + File.separator
525                     + "build.properties";
526             FileOutputStream fops = new FileOutputStream(s);
527             build.store(fops, "--- properties for Torque build ---");
528             fops.flush();
529             fops.close();
530             logger.info("Generated " + s);
531             pursue = true;
532         }
533         catch (IOException e)
534         {
535             logger.error("Error writing Torque's build.properties");
536             logger.error(e.getMessage());
537             pursue = false;
538         }
539     }
540     /***
541      *  Transform the model to XML
542      *
543      * @param  pDatabase              the database
544      * @throws  SAXException          SAXException
545      * @throws  IOException           IOException
546      * @throws  TransformerException  TransformerException
547      */
548     public  void writeXML(final PhysicalModel pDatabase)
549         throws TransformerException, SAXException, IOException
550     {
551         File tmpModel = File.createTempFile("gen", "pdm");
552         NOFileManager.save(pDatabase, tmpModel);
553         //NOFileManager.
554         String path = "xsl/pdm2torque.xsl";
555         ClassLoader cl = NextObjects.getReference().getClass().getClassLoader();
556         URL url = cl.getResource(path);
557         InputSource xslSource = new InputSource(url.toExternalForm());
558         InputSource xmlSource = new InputSource(tmpModel.getAbsolutePath());
559         FileOutputStream out =
560             new FileOutputStream(
561                 NOPreferences.getProperty("nextobjects.io.torque.home")
562                     + File.separator
563                     + "schema"
564                     + File.separator
565                     + pDatabase.getName()
566                     + "-schema.xml");
567         // Instantiate a TransformerFactory.
568         TransformerFactory tFactory = TransformerFactory.newInstance();
569         // Determine whether the TransformerFactory supports The use of
570         // SAXSource and SAXResult
571         if (tFactory.getFeature(SAXSource.FEATURE)
572             && tFactory.getFeature(SAXResult.FEATURE))
573         {
574             // Cast the TransformerFactory.
575             SAXTransformerFactory saxTFactory =
576                 ((SAXTransformerFactory) tFactory);
577             // Create a ContentHandler to handle parsing of the stylesheet.
578             TemplatesHandler templatesHandler =
579                 saxTFactory.newTemplatesHandler();
580             // Create an XMLReader and set its ContentHandler.
581             XMLReader reader = XMLReaderFactory.createXMLReader();
582             reader.setContentHandler(templatesHandler);
583             NOErrorHandler erh = new NOErrorHandler();
584             reader.setErrorHandler((ErrorHandler) erh);
585             reader.setEntityResolver(new NODTDResolver());
586             // Parse the stylesheet
587             reader.parse(xslSource);
588             //Get the Templates object from the ContentHandler.
589             Templates templates = templatesHandler.getTemplates();
590             // Create a ContentHandler to handle parsing of the XML source
591             TransformerHandler handler =
592                 saxTFactory.newTransformerHandler(templates);
593             // Reset the XMLReader's ContentHandler.
594             reader.setContentHandler(handler);
595             // Set the ContentHandler to also function as a LexicalHandler,
596             //  which includes "lexical" events (e.g., comments and CDATA).
597             reader.setProperty(
598                 "http://xml.org/sax/properties/lexical-handler",
599                 handler);
600             Serializer serializer =
601                 SerializerFactory.getSerializer(
602                     OutputPropertiesFactory.getDefaultMethodProperties("xml"));
603             serializer.setOutputStream(out);
604             // Set the result handling to be a serialization
605             //  to the file output stream.
606             Result result = new SAXResult(serializer.asContentHandler());
607             handler.setResult(result);
608             // Parse the XML input document.
609             reader.parse(xmlSource);
610         }
611         else
612         {
613             logger.error("The TransformerFactory does not support SAX I/O");
614         }
615     }
616 }