View Javadoc

1   /* Version 1.0 based on Apache Software License 1.1
2    *
3    * Copyright (c) 2003 Piotr Maj and DBMonster developers. All rights
4    * reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are
8    * met:
9    *
10   * 1. Redistributions of source code must retain the above copyright
11   *    notice, this list of conditions and the following disclaimer.
12   *
13   * 2. Redistributions in binary form must reproduce the above copyright
14   *    notice, this list of conditions and the following disclaimer in the
15   *    documentation and/or other materials provided with the distribution.
16   *
17   * 3. The end-user documentation included with the redistribution, if any,
18   *    must include the following acknowledgment:
19   *
20   *    "This product includes software developed by DBMonster developers
21   *    (http://dbmonster.kernelpanic.pl/)."
22   *
23   *  Alternately, this acknowledgment may appear in the software itself,
24   *  if and wherever such third-party acknowledgments normally appear.
25   *
26   * 4. The name "DBMonster" must not be used to endorse or promote products
27   *    derived from this software without prior written permission. For
28   *    written permission, please contact piotr.maj@kernelpanic.pl.
29   *
30   * 5. Products derived from this software may not be called "DBMonster",
31   *    nor may "DBMonster" appear in their name, without prior written
32   *    permission of Piotr Maj.
33   *
34   * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
35   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
37   * IN NO EVENT SHALL THE DBMONSTER DEVELOPERS BE LIABLE FOR ANY DIRECT,
38   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
43   * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
44   * POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package pl.kernelpanic.dbmonster.schema;
48  
49  import java.sql.Connection;
50  import java.sql.DatabaseMetaData;
51  import java.sql.PreparedStatement;
52  import java.sql.ResultSet;
53  import java.util.ArrayList;
54  import java.util.HashMap;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Map;
58  
59  import org.apache.commons.collections.iterators.FilterIterator;
60  import org.apache.commons.logging.Log;
61  
62  import pl.kernelpanic.dbmonster.DBMonster;
63  import pl.kernelpanic.dbmonster.DBMonsterContext;
64  import pl.kernelpanic.dbmonster.ProgressMonitor;
65  import pl.kernelpanic.dbmonster.connection.ConnectionProvider;
66  import pl.kernelpanic.dbmonster.connection.Transaction;
67  
68  /***
69   * The table.
70   *
71   * @author Piotr Maj <piotr.maj@kernelpanic.pl>
72   *
73   * @version $Id: Table.html,v 1.1 2007/06/21 08:38:14 sbahloul Exp $
74   */
75  public class Table implements Comparable {
76  
77      /***
78       * The name of the table.
79       */
80      private String name = null;
81  
82      /***
83       * The schema which owns the table.
84       */
85      private Schema schema = null;
86  
87      /***
88       * A primary key of the table.
89       */
90      private Key key = null;
91  
92      /***
93       * A map of columns.
94       */
95      private List columns = new ArrayList();
96  
97      /***
98       * DBMonsterContext.
99       */
100     private DBMonsterContext context = null;
101 
102     /***
103      * Progress monitor.
104      */
105     private ProgressMonitor progressMonitor = null;
106 
107     /***
108      * The number of rows to generate.
109      */
110     private int rows = 0;
111 
112     /***
113      * Statement.
114      */
115     private String query = null;
116 
117     /***
118      * The number of retries.
119      */
120     private long maxTries = 1000L;
121 
122     /***
123      * Is progress monitor set?
124      */
125     private boolean monitorSet = false;
126 
127     /***
128      * Column types hash.
129      */
130     private Map columnTypes = new HashMap();
131 
132     /***
133      * Returns the name of the table.
134      *
135      * @return the name of the table.
136      */
137     public final String getName() {
138         return name;
139     }
140 
141     /***
142      * Sets the name of the table.
143      *
144      * @param s the name of the table.
145      */
146     public final void setName(final String s) {
147         name = s;
148     }
149 
150     /***
151      * Returns schema.
152      *
153      * @return the schema
154      */
155     public final Schema getSchema() {
156         return schema;
157     }
158 
159     /***
160      * Sets the schema - it cannot be null.
161      *
162      * @param s a schema
163      */
164     public final void setSchema(final Schema s) {
165         schema = s;
166     }
167 
168     /***
169      * Status of table generation.
170      */
171     private boolean generated = false;
172 
173     private ConnectionProvider connectionProvider;
174 
175     /***
176      * Adds a column to the schema.
177      *
178      * @param column the column to add
179      *
180      * @throws SchemaException on schema error.
181      */
182     public final void addColumn(final Column column) throws SchemaException {
183         if (column == null) {
184             throw new SchemaException("Column is null!"); //$NON-NLS-1$
185         }
186         String columnName = column.getName();
187         if (containsColumn(columnName)) {
188             throw new SchemaException("Column <" + columnName + "> already exists.");
189         }
190         column.setTable(this);
191         columns.add(column);
192     }
193 
194     /***
195      * Sets the key.
196      *
197      * @param k key
198      */
199     public final void setKey(final Key k) {
200         key = k;
201         if (key != null) {
202             key.setTable(this);
203         }
204     }
205 
206     /***
207      * Returns a key.
208      *
209      * @return a key
210      */
211     public final Key getKey() {
212         return key;
213     }
214 
215     /***
216      * Initializes the table.
217      *
218      * @param ctx dbmonster context.
219      *
220      * @throws Exception if initialization fails
221      *
222      */
223     public final void initialize(final DBMonsterContext ctx) throws Exception {
224         context = ctx;
225 
226         Log log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
227 
228         // get column types hash
229         connectionProvider = (ConnectionProvider) context.getProperty(
230                 DBMonster.CONNECTION_PROVIDER_KEY);
231         Connection conn = null;
232         try {
233             if (log.isDebugEnabled()) {
234                 log.debug("Getting column types.");
235             }
236 
237             conn = connectionProvider.getConnection();
238             DatabaseMetaData metaData = conn.getMetaData();
239             String schemaName = (String) ctx.getProperty("dbmonster.jdbc.schema");
240             ResultSet rs = metaData.getColumns(null, schemaName, name, null);
241 
242             String columnName = null;
243             int columnType = 0;
244             while (rs.next()) {
245                 columnName = rs.getString("COLUMN_NAME");
246                 columnType = rs.getInt("DATA_TYPE");
247                 columnTypes.put(columnName, new Integer(columnType));
248                 if (log.isDebugEnabled()) {
249                     log.debug("Column: " + columnName + ", SQL type: " + columnType);
250                 }
251             }
252         } catch (Exception e) {
253             throw new Exception("Couldn't get column types from database. Buggy JDBC driver?");
254         } finally {
255             if (conn != null) {
256                 conn.close();
257             }
258         }
259         String lng = (String) context.getProperty("dbmonster.max-tries"); //$NON-NLS-1$
260         try {
261             maxTries = Long.parseLong(lng);
262         } catch (Exception e) {
263             if (log.isDebugEnabled()) {
264                 log.debug("Unable to parse dbmonster.max-tries." //$NON-NLS-1$
265                     + " Using default value of 1000."); //$NON-NLS-1$
266             }
267         }
268 
269         progressMonitor =
270             (ProgressMonitor) context.getProperty(
271                 DBMonster.PROGRESS_MONITOR_KEY);
272         if (log.isDebugEnabled()) {
273             log.debug("Initializing table <" + name + ">."); //$NON-NLS-1$ //$NON-NLS-2$
274         }
275 
276         StringBuffer sql = new StringBuffer("INSERT INTO "); //$NON-NLS-1$
277         sql.append(name);
278         sql.append(" ("); //$NON-NLS-1$
279 
280         int count = 0;
281         if (key != null && !key.getDatabaseDefault()) {
282             key.initialize(ctx);
283             List cols = key.getGenerator().getColumns();
284             for (int i = 0; i < cols.size(); i++) {
285                 Column column = (Column) cols.get(i);
286                 if (i != 0) {
287                     sql.append(", "); //$NON-NLS-1$
288                 }
289                 sql.append(column.getName());
290                 ++count;
291             }
292         }
293 
294         Iterator it = columnIterator();
295         if (it.hasNext()) {
296             if (key != null && !key.getDatabaseDefault()) {
297                 sql.append(", "); //$NON-NLS-1$
298             }
299         }
300 
301         boolean first = true;
302         while (it.hasNext()) {
303             Column column = (Column) it.next();
304             column.initialize(ctx);
305             if (!first) {
306                 sql.append(", "); //$NON-NLS-1$
307             }
308             sql.append(column.getName());
309             first = false;
310             ++count;
311         }
312 
313         sql.append(") VALUES ("); //$NON-NLS-1$
314         while (--count >= 0) {
315             sql.append("?"); //$NON-NLS-1$
316             if (count != 0) {
317                 sql.append(", "); //$NON-NLS-1$
318             }
319         }
320         sql.append(")"); //$NON-NLS-1$
321 
322         query = sql.toString();
323 
324         if (log.isDebugEnabled()) {
325             log.debug("Query: " + query); //$NON-NLS-1$
326             log.debug("Initialization of table <" + name + "> finished."); //$NON-NLS-1$ //$NON-NLS-2$
327         }
328     }
329 
330     /***
331      * Generates the table.
332      *
333      * @throws Exception if table generation fails
334      */
335     public final void generate() throws Exception {
336         Log log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
337 
338         if (!generated) {
339 
340             if (log.isInfoEnabled()) {
341                 log.info("Generating table <" + name + ">."); //$NON-NLS-1$ //$NON-NLS-2$
342             }
343 
344             int counter = 0;
345             List keyColumns = null;
346             Transaction tx = null;
347             try {
348                 tx = new Transaction(connectionProvider);
349                 tx.begin();
350                 PreparedStatement pstmt = tx.prepareStatement(query);
351                 int currentTry = 0;
352                 while (counter < rows) {
353                     if (Thread.interrupted()) {
354                         throw new InterruptedException();
355                     }
356                     pstmt.clearParameters();
357                     if (key != null && !key.getDatabaseDefault()) {
358                         if (log.isDebugEnabled()) {
359                             log.debug("Generating key: " + key); //$NON-NLS-1$
360                         }
361                         keyColumns = key.generate();
362                     }
363                     Iterator it = columnIterator();
364                     while (it.hasNext()) {
365                         ((Column) it.next()).generate();
366                     }
367 
368                     if (!monitorSet) {
369                         if (progressMonitor != null) {
370                             progressMonitor.setTableName(name);
371                         }
372                         if (progressMonitor != null) {
373                             progressMonitor.setRowsCount(rows);
374                         }
375                         monitorSet = true;
376                     }
377 
378                     int paramCount = 1;
379                     if (key != null && !key.getDatabaseDefault()) {
380                         it = keyColumns.iterator();
381                         while (it.hasNext()) {
382                             Column column = (Column) it.next();
383                             if (log.isDebugEnabled()) {
384                                 log.debug("Key column parameter " + paramCount //$NON-NLS-1$
385                                     + " is " + column.getValue() + "."); //$NON-NLS-1$ //$NON-NLS-2$
386                             }
387                             Integer columnType = ((Integer) columnTypes.get(column.getName()));
388                             if (columnType != null) {
389                                 pstmt.setObject(paramCount++, column.getValue(), columnType.intValue());
390                             } else {
391                                 pstmt.setObject(paramCount++, column.getValue());
392                             }
393                         }
394                     }
395 
396                     it = columnIterator();
397                     while (it.hasNext()) {
398                         Column column = (Column) it.next();
399                         if (log.isDebugEnabled()) {
400                             log.debug("Column parameter " + paramCount //$NON-NLS-1$
401                                 + " is " + column.getValue() + "."); //$NON-NLS-1$ //$NON-NLS-2$
402                         }
403                         Integer columnType = ((Integer) columnTypes.get(column.getName()));
404                         if (columnType != null) {
405                             pstmt.setObject(paramCount++, column.getValue(), columnType.intValue());
406                         } else {
407                             pstmt.setObject(paramCount++, column.getValue());
408                         }
409                     }
410                     try {
411                         tx.execute();
412                         counter++;
413                         if (progressMonitor != null) {
414                             progressMonitor.rowComplete();
415                         }
416                     } catch (Exception e) {
417                         if (log.isDebugEnabled()) {
418                             log.debug("Execution failed for reason: " //$NON-NLS-1$
419                                 + e.getMessage());
420                        }
421                        currentTry++;
422                        if (currentTry > maxTries) {
423                             throw new Exception("Max tries exceeded! Quiting."); //$NON-NLS-1$
424                         }
425                     }
426                     resetColumns();
427                 }
428                 
429                 tx.commit();
430             } catch (Exception e) {
431                 tx.abort();
432                 throw e;
433             } finally {
434                 tx.close();
435             }
436             generated = true;
437             if (progressMonitor != null) {
438                 progressMonitor.tableComplete();
439             }
440             if (log.isInfoEnabled()) {
441                 log.info("Generation of table <" + name + "> finished."); //$NON-NLS-1$ //$NON-NLS-2$
442             }
443         }
444     }
445 
446     /***
447      * Resets key and all columns.
448      */
449     public final void resetColumns() {
450         Iterator it = columnIterator();
451         while (it.hasNext()) {
452             ((Column) it.next()).reset();
453         }
454     }
455 
456     /***
457      * Resets the entire table and mark it as if it was not generated.
458      */
459     public final void reset() {
460         resetColumns();
461         for (int i = 0; i < columns.size(); i++) {
462             Column c = (Column) columns.get(i);
463             c.getGenerator().reset();
464         }
465         generated = false;
466         monitorSet = false;
467     }
468 
469     /***
470      * Returns the number of rows we want to generate for this table.
471      *
472      * @return number of rows
473      */
474     public final int getRows() {
475         return rows;
476     }
477 
478     /***
479      * Sets the number of rows.
480      *
481      * @param i number of rows
482      */
483     public final void setRows(final int i) {
484         rows = i;
485     }
486 
487     /***
488      * Checks if the table is already generated.
489      *
490      * @return <code>true</code> if the table is already generated.
491      */
492     public final boolean isGenerated() {
493         return generated;
494     }
495 
496     /***
497      * Returns a column iterator.
498      *
499      * @return iterator over columns
500      */
501     public final Iterator columnIterator() {
502         return new FilterIterator(columns.iterator(), new ColumnPredicate());
503     }
504 
505     /***
506      * Returns list of columns.
507      *
508      * @return list of columns
509      */
510     public final List getColumns() {
511         return columns;
512     }
513 
514     /***
515      * @see java.lang.Comparable#compareTo(java.lang.Object)
516      */
517     public final int compareTo(final Object o) {
518         Table t = (Table) o;
519         return getName().compareTo(t.getName());
520     }
521 
522     /***
523      * Removes column from table.
524      *
525      * @param column column to remove
526      */
527     public final void removeColumn(final Column column) {
528         for (int i = 0; i < columns.size(); i++) {
529             Column c = (Column) columns.get(i);
530             if (c == column) {
531                 columns.remove(i);
532                 break;
533             }
534         }
535     }
536 
537     public boolean containsColumn(String name) {
538         for (int i = 0; i < columns.size(); i++) {
539             Column column = (Column) columns.get(i);
540             if (name.equals(column.getName())) {
541                 return true;
542             }
543         }
544         return false;
545     }
546 
547     public Column findColumn(String name) {
548         Column c = null;
549         for (int i = 0; i < columns.size(); i++) {
550             c = (Column) columns.get(i);
551             if (c.getName().equals(name)) {
552                 return c;
553             }
554             c = null;
555         }
556         return c;
557     }
558 }