View Javadoc
1   /*
2    * This file is a part of the SchemaSpy project (http://schemaspy.sourceforge.net).
3    * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 John Currier
4    *
5    * SchemaSpy is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *
10   * SchemaSpy is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package net.sourceforge.schemaspy;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.io.PrintStream;
26  import java.io.Reader;
27  import java.sql.DatabaseMetaData;
28  import java.sql.SQLException;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.logging.Level;
34  import java.util.logging.Logger;
35  import java.util.regex.Pattern;
36  import net.sourceforge.schemaspy.model.ProcessExecutionException;
37  import net.sourceforge.schemaspy.util.LineWriter;
38  import net.sourceforge.schemaspy.view.HtmlMultipleSchemasIndexPage;
39  
40  /**
41   * @author John Currier
42   */
43  public final class MultipleSchemaAnalyzer {
44      private static MultipleSchemaAnalyzer instance = new MultipleSchemaAnalyzer();
45      private final Logger logger = Logger.getLogger(getClass().getName());
46      private final boolean fineEnabled = logger.isLoggable(Level.FINE);
47  
48      private MultipleSchemaAnalyzer() {
49      }
50  
51      public static MultipleSchemaAnalyzer getInstance() {
52          return instance;
53      }
54  
55      public void analyze(String dbName, DatabaseMetaData meta, String schemaSpec, List<String> schemas, List<String> args, String user, File outputDir, String charset, String loadedFrom) throws SQLException, IOException {
56          long start = System.currentTimeMillis();
57          List<String> genericCommand = new ArrayList<String>();
58          genericCommand.add("java");
59          genericCommand.add("-Doneofmultipleschemas=true");
60          if (new File(loadedFrom).isDirectory()) {
61              genericCommand.add("-cp");
62              genericCommand.add(loadedFrom);
63              genericCommand.add(Main.class.getName());
64          } else {
65              genericCommand.add("-jar");
66              genericCommand.add(loadedFrom);
67          }
68  
69          for (String next : args) {
70              if (next.startsWith("-"))
71                  genericCommand.add(next);
72              else
73                  genericCommand.add("\"" + next + "\"");
74          }
75  
76          List<String> populatedSchemas;
77          if (schemas == null) {
78              System.out.println("Analyzing schemas that match regular expression '" + schemaSpec + "':");
79              System.out.println("(use -schemaSpec on command line or in .properties to exclude other schemas)");
80              populatedSchemas = getPopulatedSchemas(meta, schemaSpec, user);
81          } else {
82              System.out.println("Analyzing schemas:");
83              populatedSchemas = schemas;
84          }
85  
86          for (String populatedSchema : populatedSchemas)
87              System.out.print(" " + populatedSchema);
88          System.out.println();
89  
90          writeIndexPage(dbName, populatedSchemas, meta, outputDir, charset);
91  
92          for (String schema : populatedSchemas) {
93              List<String> command = new ArrayList<String>(genericCommand);
94              if (dbName == null)
95                  command.add("-db");
96              else
97                  command.add("-s");
98              command.add(schema);
99              command.add("-o");
100             command.add(new File(outputDir, schema).toString());
101             System.out.println("Analyzing " + schema);
102             System.out.flush();
103             Process java = Runtime.getRuntime().exec(command.toArray(new String[]{}));
104             new ProcessOutputReader(java.getInputStream(), System.out).start();
105             new ProcessOutputReader(java.getErrorStream(), System.err).start();
106 
107             try {
108                 int rc = java.waitFor();
109                 if (rc != 0) {
110                     StringBuilder err = new StringBuilder("Failed to execute this process (rc " + rc + "):");
111                     for (String chunk : command) {
112                         err.append(" ");
113                         err.append(chunk);
114                     }
115                     throw new ProcessExecutionException(err.toString());
116                 }
117             } catch (InterruptedException exc) {
118             }
119         }
120 
121         long end = System.currentTimeMillis();
122         System.out.println();
123         System.out.println("Wrote relationship details of " + populatedSchemas.size() + " schema" + (populatedSchemas.size() == 1 ? "" : "s") + " in " + (end - start) / 1000 + " seconds.");
124         System.out.println("Start with " + new File(outputDir, "index.html"));
125     }
126 
127     public void analyze(String dbName, List<String> schemas, List<String> args,
128             String user, File outputDir, String charset, String loadedFromJar) throws SQLException, IOException {
129         analyze(dbName, null, null, schemas, args, user, outputDir, charset, loadedFromJar);
130     }
131 
132    private void writeIndexPage(String dbName, List<String> populatedSchemas, DatabaseMetaData meta, File outputDir, String charset) throws IOException {
133         if (populatedSchemas.size() > 0) {
134             LineWriter index = new LineWriter(new File(outputDir, "index.html"), charset);
135             HtmlMultipleSchemasIndexPage.getInstance().write(dbName, populatedSchemas, meta, index);
136             index.close();
137         }
138     }
139 
140     private List<String> getPopulatedSchemas(DatabaseMetaData meta, String schemaSpec, String user) throws SQLException {
141         List<String> populatedSchemas;
142 
143         if (meta.supportsSchemasInTableDefinitions()) {
144             Pattern schemaRegex = Pattern.compile(schemaSpec);
145 
146             populatedSchemas = DbAnalyzer.getPopulatedSchemas(meta, schemaSpec);
147             Iterator<String> iter = populatedSchemas.iterator();
148             while (iter.hasNext()) {
149                 String schema = iter.next();
150                 if (!schemaRegex.matcher(schema).matches()) {
151                     if (fineEnabled) {
152                         logger.fine("Excluding schema " + schema +
153                                     ": doesn't match + \"" + schemaRegex + '"');
154                     }
155                     iter.remove(); // remove those that we're not supposed to analyze
156                 } else {
157                     if (fineEnabled) {
158                         logger.fine("Including schema " + schema +
159                                     ": matches + \"" + schemaRegex + '"');
160                     }
161                 }
162             }
163         } else {
164             populatedSchemas = Arrays.asList(new String[] {user});
165         }
166 
167         return populatedSchemas;
168     }
169 
170     private static class ProcessOutputReader extends Thread {
171         private final Reader processReader;
172         private final PrintStream out;
173 
174         ProcessOutputReader(InputStream processStream, PrintStream out) {
175             processReader = new InputStreamReader(processStream);
176             this.out = out;
177             setDaemon(true);
178         }
179 
180         @Override
181         public void run() {
182             try {
183                 int ch;
184                 while ((ch = processReader.read()) != -1) {
185                     out.print((char)ch);
186                     out.flush();
187                 }
188             } catch (IOException ioException) {
189                 ioException.printStackTrace();
190             } finally {
191                 try {
192                     processReader.close();
193                 } catch (Exception exc) {
194                     exc.printStackTrace(); // shouldn't ever get here...but...
195                 }
196             }
197         }
198     }
199 }