1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.serializer;
18
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileWriter;
23 import java.io.FilenameFilter;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import javax.xml.transform.Result;
30 import javax.xml.transform.Source;
31 import javax.xml.transform.Transformer;
32 import javax.xml.transform.TransformerFactory;
33 import javax.xml.transform.stream.StreamResult;
34 import javax.xml.transform.stream.StreamSource;
35
36 import javolution.xml.XMLBinding;
37 import javolution.xml.XMLObjectReader;
38
39 import org.apache.commons.configuration.PropertiesConfiguration;
40 import org.apache.ddlutils.model.Database;
41 import org.apache.ddlutils.model.Table;
42 import org.apache.jetspeed.serializer.objects.JSGroup;
43 import org.apache.log4j.Level;
44 import org.apache.log4j.Logger;
45 /***
46 * Jetspeed Serializer DDL- Application
47 *
48 * invoke with mandatory either
49 * <p>
50 * -I directory or filename of schema files for input, if directory all xml
51 * files will be processed
52 * </p>
53 * and/or
54 * <p>
55 * -O schema file for output log of current database
56 * </p>
57 * and (if -I denotes a directory:)
58 * <p>
59 * -x if directory list provided for input schemas this pattern is excluded
60 * </p>
61 * <p>
62 * -s name of the order file (if schema directory)
63 * </p>
64 *
65 * <p>
66 * -m if directory list provided this is the merge file to use. If not set here
67 * or in the properties file, a hardcoded version is used
68 * </p>
69 *
70 * <p>
71 * note that - if -I and -O are specified, the output file will contain the
72 * UPDATED database
73 * </p>
74 * invoke with (optional) parameters
75 * <p>
76 * -P propertyFileName, for settings
77 * <p>
78 * -R (flag) replace : overwrites default "UPDATE" and clears out the database
79 * before processing (ignored with -O option above)
80 * </p>
81 *
82 * <p>
83 * -dn databaseName, for example MYSQL or ORACLE10
84 * </p>
85 * <p>
86 * -dc driverClass, for example com.mysql.jdbc.Driver
87 * </p>
88 * <p>
89 * -ds url, ruls according to the driver used, URL needs to point to the correct
90 * database
91 * </p>
92 * <p>
93 * -du user, user with create/drop etc. rights on the database
94 * </p>
95 * <p>
96 * -dp password
97 * </p>
98 * <p>
99 * -l log4j-level, ERROR (default), WARN, INFO
100 * </p>
101 *
102 * @author <a href="mailto:hajo@bluesunrise.com">Hajo Birthelmer</a>
103 * @version $Id: $
104 */
105 public class JetspeedDDLApplication
106 {
107 public static final String JNDI_DS_NAME = "jetspeed";
108
109 String propertyFileName = null;
110 String exludeFileName = null;
111 String orderFileName = null;
112
113 String logLevel = null;
114
115 PropertiesConfiguration configuration = null;
116
117 boolean doImport = false;
118 boolean doExport = false;
119 String schemaDirectory = null;
120
121 String outputFile = null;
122
123 boolean overwrite = false;
124
125 String driverClass = null;
126 String url = null;
127 String user = null;
128 String password = null;
129 String databaseName = null;
130
131 String[] filesToProcess = null;
132
133 String mergeFile = null;
134 String[] args = null;
135
136 public static void main(String[] args) throws Exception
137 {
138 JetspeedDDLApplication app = new JetspeedDDLApplication();
139 app.processArguments(args);
140 }
141
142 public JetspeedDDLApplication()
143 {
144 }
145
146 /***
147 * ensure that we have valid database settings
148 *
149 */
150 private void checkDBSettings()
151 {
152 if (databaseName == null)
153 databaseName = System.getProperty(
154 "org.apache.jetspeed.database.databaseName",
155 "mysql");
156 if (driverClass == null)
157 driverClass = System.getProperty(
158 "org.apache.jetspeed.database.driverClass",
159 "com.mysql.jdbc.Driver");
160 if (url == null)
161 url = System.getProperty("org.apache.jetspeed.database.url",
162 "jdbc:mysql://localhost/j2test");
163 if (user == null)
164 user = System.getProperty("org.apache.jetspeed.database.user",
165 "user");
166 if (password == null)
167 password = System.getProperty(
168 "org.apache.jetspeed.database.password", "password");
169
170 if (driverClass == null)
171 throw new IllegalArgumentException(
172 "Can't proceed without a valid driver");
173 if (url == null)
174 throw new IllegalArgumentException(
175 "Can't proceed without a valid url to the target database");
176 if (user == null)
177 throw new IllegalArgumentException(
178 "Can't proceed without a valid database user");
179 return;
180 }
181
182 /***
183 * parse arguments for process instructions, order and exclude files as well
184 * as optional database arguments
185 *
186 */
187 private void parseArguments()
188 {
189
190 for (int n = 0; n < args.length; n++)
191 {
192 if (args[n].equals("-I"))
193 {
194 doImport = true;
195 schemaDirectory = args[++n];
196 } else if (args[n].equals("-O"))
197 {
198 doExport = true;
199 outputFile = args[++n];
200 } else if (args[n].equals("-s"))
201 {
202 orderFileName = args[++n];
203 }
204 else if (args[n].equals("-x"))
205 {
206 exludeFileName = args[++n];
207 }
208 else if (args[n].equals("-m"))
209 {
210 mergeFile = args[++n];
211 }
212 else if (args[n].equals("-R"))
213 overwrite = true;
214 else if (args[n].equals("-dn"))
215 {
216 databaseName = args[++n];
217 }
218 else if (args[n].equals("-dc"))
219 driverClass = args[++n];
220 else if (args[n].equals("-ds"))
221 url = args[++n];
222 else if (args[n].equals("-du"))
223 {
224 if (((n + 1) >= args.length) || args[n + 1].startsWith("-d"))
225 {
226 user = "";
227 } else
228 {
229 user = args[++n];
230 }
231 }
232 else if (args[n].equals("-dp"))
233 {
234 if (((n + 1) >= args.length) || args[n + 1].startsWith("-d"))
235 {
236 password = "";
237 } else
238 {
239 password = args[++n];
240 }
241 }
242 else if (args[n].equals("-P"))
243 propertyFileName = args[++n];
244 else if (args[n].equals("-l"))
245 logLevel = args[++n];
246
247 else
248 throw new IllegalArgumentException("Unknown argument: "
249 + args[n]);
250 }
251
252 }
253
254 /***
255 * process provided filename or directory name
256 *
257 * @return one or more files to be processed
258 */
259 private String[] parseFiles()
260 {
261 String[] fileList = null;
262 try
263 {
264 File dir = new File(schemaDirectory);
265 if (!(dir.exists()))
266 return fileList;
267 if (!(dir.isDirectory()))
268 {
269 fileList = new String[1];
270 fileList[0] = schemaDirectory;
271 return fileList;
272 }
273
274 LocalFilenameFilter filter = new LocalFilenameFilter(
275 exludeFileName, orderFileName);
276 File[] files = dir.listFiles(filter);
277 if (files == null)
278 return fileList;
279
280 fileList = new String[files.length];
281 String sortorderFile = filter.getSortFile();
282 if (sortorderFile == null)
283 {
284 for (int i = 0; i < files.length; i++)
285 fileList[i] = files[i].getAbsolutePath();
286 return fileList;
287 }
288 try
289 {
290 ArrayList list = readOrderFile(sortorderFile);
291 fileList = new String[files.length];
292 if ((list == null) || (list.size() == 0))
293 {
294 for (int i = 0; i < files.length; i++)
295 fileList[i] = files[i].getAbsolutePath();
296 return fileList;
297 }
298 String[] tempList = new String[files.length];
299 for (int i = 0; i < files.length; i++)
300 tempList[i] = files[i].getName();
301 Iterator _it = list.iterator();
302 int j = 0;
303 while (_it.hasNext())
304 {
305 String filename = null;
306 try
307 {
308 filename = ((JSGroup) _it.next()).getName();
309 } catch (Exception eeee)
310 {
311 }
312 if (filename != null)
313 {
314 for (int i = 0; i < files.length; i++)
315 {
316 if (filename.equalsIgnoreCase(tempList[i]))
317 {
318 fileList[j++] = files[i].getAbsolutePath();
319 tempList[i] = null;
320 }
321 }
322 }
323 }
324 for (int i = 0; i < files.length; i++)
325 {
326 if (tempList[i] != null)
327 fileList[j++] = files[i].getAbsolutePath();
328 }
329 return fileList;
330 } catch (Exception eee)
331 {
332 eee.printStackTrace();
333 return null;
334 }
335
336 } catch (Exception e)
337 {
338 e.printStackTrace();
339 throw new IllegalArgumentException(
340 "Processing the schema-directory " + schemaDirectory
341 + " caused exception " + e.getLocalizedMessage());
342 }
343
344 }
345
346 /***
347 * setup environment by processing all arguments and call requested process
348 * routine
349 *
350 * @param arguments
351 * @throws Exception
352 */
353 private void processArguments(String[] arguments) throws Exception
354 {
355 this.args = arguments;
356 if (args == null)
357 throw new IllegalArgumentException(
358 "Either a schema directory, a schema file or an output filename have to be defined (-D followed by a driectory, -I or -O followed by the filename");
359
360 parseArguments();
361
362 processPropertyFile();
363
364 checkDBSettings();
365
366 /***
367 * The only required argument is the filename for either export or
368 * import
369 */
370 if ((!doImport) && (!doExport))
371 throw new IllegalArgumentException(
372 "Either a schema directory, a schema file or an output filename have to be defined (-I or -O followed by the directory-/filename");
373
374 if (doImport)
375 {
376 filesToProcess = parseFiles();
377 if (filesToProcess == null)
378 return;
379 }
380
381 /*** create the instruction map */
382 JetspeedDDLUtil ddlUtil = null;
383
384 HashMap context = new HashMap();
385 context.put(JetspeedDDLUtil.DATASOURCE_DATABASENAME, databaseName);
386 context.put(JetspeedDDLUtil.DATASOURCE_DRIVER, driverClass);
387 context.put(JetspeedDDLUtil.DATASOURCE_URL, url);
388 context.put(JetspeedDDLUtil.DATASOURCE_USERNAME, user);
389 context.put(JetspeedDDLUtil.DATASOURCE_PASSWORD, password);
390
391 Logger logger = Logger.getLogger("org.apache.ddlutils");
392 Level level = logger.getLevel();
393 if (logLevel == null)
394 logger.setLevel(Level.ERROR);
395 else if (logLevel.equalsIgnoreCase("INFO"))
396 logger.setLevel(Level.INFO);
397 else if (logLevel.equalsIgnoreCase("WARN"))
398 logger.setLevel(Level.WARN);
399 else
400 logger.setLevel(Level.ERROR);
401
402 try
403 {
404 ddlUtil = new JetspeedDDLUtil();
405 ddlUtil.startUp();
406 ddlUtil.init(context);
407 } catch (Exception e)
408 {
409 System.err.println("Failed to initialize Utility!!!!!");
410 e.printStackTrace();
411 System.exit(-1);
412 }
413 try
414 {
415 if (doImport)
416 processImport(ddlUtil);
417 if (doExport)
418 processExport(ddlUtil);
419 } catch (Exception e)
420 {
421 System.err.println("Failed to process XML "
422 + (doExport ? "export" : "import") + ":" + e);
423 e.printStackTrace();
424 } finally
425 {
426 try
427 {
428 logger.setLevel(level);
429 if (ddlUtil != null)
430 ddlUtil.tearDown();
431 } catch (Exception e1)
432 {
433 System.out
434 .println("starter framework teardown caused exception "
435 + e1.getLocalizedMessage());
436 e1.printStackTrace();
437
438 }
439 }
440
441 }
442
443 /***
444 * create/alter database
445 *
446 * @param ddlUtil
447 */
448 private void processImport(JetspeedDDLUtil ddlUtil)
449 {
450 String file = null;
451 if ((filesToProcess == null) || (filesToProcess.length == 0))
452 return;
453 if (filesToProcess.length > 1)
454 file = mergeFiles(filesToProcess);
455
456 System.out.println("Importing " + file);
457 Database db = ddlUtil.createDatabaseSchemaFromXML(file);
458 try
459 {
460 if (overwrite)
461 ddlUtil.createDatabase(db);
462
463 else
464 ddlUtil.alterDatabase(db);
465 System.out.println("Importing " + file + " completed");
466 } catch (Exception ePr)
467 {
468 ePr.printStackTrace();
469
470
471 }
472
473 }
474
475 /***
476 * Helper routine to create a temporary file
477 *
478 * @param suffix
479 * @return
480 */
481 private File createTemp(String suffix)
482 {
483 try
484 {
485
486 File temp = File.createTempFile("tmp", suffix);
487
488
489 temp.deleteOnExit();
490 return temp;
491 } catch (IOException e)
492 {
493 System.out.println("Failed to create temproary file with "
494 + e.getLocalizedMessage());
495 e.printStackTrace();
496 return null;
497 }
498
499 }
500 /***
501 * Open the merge file from disk
502 *
503 * @param fileName
504 * @return
505 */
506 private File createXSLTFromFile(String fileName)
507 {
508 if (fileName == null)
509 return null;
510 try
511 {
512 File f = new File(fileName);
513 if (f.exists())
514 return f;
515 return null;
516 }
517 catch (Exception e)
518 {
519 System.out.println("Failed to open merge template " + e.getLocalizedMessage());
520 e.printStackTrace();
521 return null;
522 }
523 }
524 /***
525 * If everything else fails, use a hardcoded XSLT here
526 *
527 * @return
528 */
529 private File createXSLTFromMemory()
530 {
531 StringBuffer buffer = new StringBuffer();
532
533 buffer.append("<?xml version=\"1.0\"?>");
534 buffer
535 .append("<xslt:transform version=\"1.0\" xmlns:xslt=\"http://www.w3.org/1999/XSL/Transform\">");
536 buffer
537 .append("<!-- Simple template to merge two database schemas into one -->");
538 buffer.append("<xslt:param name=\"fileTwo\" />");
539 buffer.append("<xslt:template match=\"/\">");
540
541 buffer.append("<xslt:message>");
542 buffer
543 .append("<xslt:text /> Merging input with '<xslt:value-of select=\"$fileTwo\"/>");
544 buffer.append("<xslt:text>'</xslt:text>");
545 buffer.append("</xslt:message>");
546 buffer.append("<xslt:if test=\"string($fileTwo)=''\">");
547 buffer.append("<xslt:message terminate=\"yes\">");
548 buffer
549 .append("<xslt:text>No input file specified (parameter 'fileTwo')</xslt:text>");
550 buffer.append("</xslt:message>");
551 buffer.append("</xslt:if>");
552 buffer.append("<database name=\"generic\">");
553 buffer.append("<xslt:apply-templates />");
554 buffer.append("</database>");
555 buffer.append("</xslt:template>");
556 buffer.append("<xslt:template match=\"database\">");
557 buffer.append("<xslt:apply-templates />");
558 buffer.append("<xslt:apply-templates select=\"document($fileTwo)/database/table\"/>");
559 buffer.append("</xslt:template>");
560
561 buffer.append("<xslt:template match=\"@*|node()\">");
562 buffer.append("<xslt:copy>");
563 buffer.append("<xslt:apply-templates select=\"@*|node()\"/>");
564 buffer.append("</xslt:copy>");
565 buffer.append("</xslt:template>");
566 buffer.append("</xslt:transform>");
567
568 File xslt = createTemp(".xslt");
569 try
570 {
571
572
573 BufferedWriter out = new BufferedWriter(new FileWriter(xslt));
574 out.write(buffer.toString());
575 out.close();
576 return xslt;
577 } catch (Exception e)
578 {
579 e.printStackTrace();
580 return null;
581 }
582 }
583 /***
584 * process of merging two or more schema files into one schema file.
585 *
586 * @param fileList The filelist contains a (potentially) ordered list of schemas
587 * @return The name of the created temporary schema file
588 */
589 private String mergeFiles(String[] fileList)
590 {
591 try
592 {
593 File xsltFile = createXSLTFromFile(mergeFile);
594 if (xsltFile == null)
595 xsltFile = createXSLTFromMemory();
596 Source xslt = new StreamSource(xsltFile);
597 Transformer transformer = TransformerFactory.newInstance()
598 .newTransformer(xslt);
599
600 String sourceName = fileList[0];
601 File target = null;
602 for (int i = 1; i < fileList.length; i++)
603 {
604 File soureFile = new File(sourceName);
605 Source source = new StreamSource(soureFile);
606
607 target = createTemp(".xml");
608
609 Result targetResult = new StreamResult(target);
610 File f = new File(fileList[i]);
611 String other = "file:///" + f.getCanonicalPath(); // required on Win-platforms
612 other = other.replace('//', '/');
613
614 transformer.setParameter("fileTwo", other);
615 transformer.transform(source, targetResult);
616 sourceName = target.getAbsolutePath();
617 }
618 return sourceName;
619
620 } catch (Exception e)
621 {
622 e.printStackTrace();
623 return null;
624 }
625
626 }
627
628 /***
629 * read database schema to file
630 *
631 */
632 private void processExport(JetspeedDDLUtil ddlUtil)
633 {
634
635 ddlUtil.writeDatabaseSchematoFile(this.outputFile);
636
637 }
638
639 /***
640 * read the property file and read what has not yet been defined
641 */
642 private void processPropertyFile()
643 {
644 /*** get system property definition */
645 if (propertyFileName == null)
646 propertyFileName = System.getProperty(
647 "org.apache.jetspeed.xml.ddlUtil.configuration", null);
648
649 if (propertyFileName == null)
650 return;
651 try
652 {
653 configuration = new PropertiesConfiguration(propertyFileName);
654 } catch (Exception e)
655 {
656 e.printStackTrace();
657 return;
658 }
659 if (configuration != null)
660 {
661 /*** only read what was not defined on the command line */
662
663 if (driverClass == null)
664 driverClass = configuration.getString("driverClass");
665 if (url == null)
666 url = configuration.getString("url");
667 if (user == null)
668 user = configuration.getString("user");
669 if (password == null)
670 password = configuration.getString("password");
671 if (mergeFile == null)
672 mergeFile = configuration.getString("mergeFile");
673 if (!(doImport))
674 {
675 schemaDirectory = configuration.getString("schema");
676 if (schemaDirectory != null)
677 doImport = true;
678 }
679 if (!(doExport))
680 {
681 outputFile = configuration.getString("outputFile");
682 if (outputFile != null)
683 doExport = true;
684 }
685 if (logLevel == null)
686 logLevel = configuration.getString("loglevel");
687
688 }
689
690 }
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710 public List getRows(JetspeedDDLUtil ddlUtil, String tableName)
711 {
712 Table table = ddlUtil.getModel().findTable(tableName,
713 ddlUtil.getPlatform().isDelimitedIdentifierModeOn());
714
715 return ddlUtil.getPlatform().fetch(ddlUtil.getModel(),
716 getSelectQueryForAllString(ddlUtil, table), new Table[]
717 {table});
718 }
719
720 public String getSelectQueryForAllString(JetspeedDDLUtil ddlUtil,
721 Table table)
722 {
723
724 StringBuffer query = new StringBuffer();
725
726 query.append("SELECT * FROM ");
727 if (ddlUtil.getPlatform().isDelimitedIdentifierModeOn())
728 {
729 query.append(ddlUtil.getPlatform().getPlatformInfo()
730 .getDelimiterToken());
731 }
732 query.append(table.getName());
733 if (ddlUtil.getPlatform().isDelimitedIdentifierModeOn())
734 {
735 query.append(ddlUtil.getPlatform().getPlatformInfo()
736 .getDelimiterToken());
737 }
738 System.out.println(query.toString());
739 return query.toString();
740 }
741
742 /***
743 * read an xml file describing the basic order of the files to be processed
744 *
745 * @param importFileName
746 * @return
747 * @throws SerializerException
748 */
749
750 private ArrayList readOrderFile(String importFileName)
751 {
752 XMLObjectReader reader = null;
753
754 XMLBinding binding = new XMLBinding();
755 binding.setAlias(ArrayList.class, "ProcessOrder");
756 binding.setAlias(JSGroup.class, "File");
757
758 ArrayList snap = null;
759 try
760 {
761 reader = XMLObjectReader.newInstance(new FileInputStream(
762 importFileName));
763 } catch (Exception e)
764 {
765 e.printStackTrace();
766 return null;
767 }
768 try
769 {
770 reader.setBinding(binding);
771 snap = (ArrayList) reader.read("ProcessOrder", ArrayList.class);
772
773 } catch (Exception e)
774 {
775 e.printStackTrace();
776 } finally
777 {
778 /*** ensure the reader is closed */
779 try
780 {
781 reader.close();
782 } catch (Exception e1)
783 {
784 /***
785 * don't do anything with this exception - never let the bubble
786 * out of the finally block
787 */
788 }
789 }
790 return snap;
791 }
792
793 class LocalFilenameFilter implements FilenameFilter
794 {
795
796 String exclude = null;
797 String sortFile = null;
798 String sort = null;
799
800 String getSortFile()
801 {
802 return sortFile;
803 }
804 LocalFilenameFilter(String exclude, String sort)
805 {
806 this.exclude = exclude;
807 this.sort = sort;
808
809 }
810 public boolean accept(File dir, String name)
811 {
812 if (exclude != null)
813 if (name.equalsIgnoreCase(exclude))
814 return false;
815 if (sort != null)
816 if (name.equalsIgnoreCase(sort))
817 {
818 sortFile = dir.getAbsolutePath() + "/" + sort;
819 return false;
820 }
821
822 return name.endsWith(".xml");
823 }
824
825 }
826
827 }