View Javadoc

1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2001-2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: RDBMSTool.java,v 1.4 2005/11/12 12:47:37 tanderson Exp $
44   */
45  package org.exolab.jms.tools.db;
46  
47  import java.sql.Connection;
48  import java.sql.DriverManager;
49  import java.sql.SQLException;
50  import java.sql.Statement;
51  
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  
55  import org.exolab.jms.config.Configuration;
56  import org.exolab.jms.config.RdbmsDatabaseConfiguration;
57  import org.exolab.jms.persistence.PersistenceException;
58  import org.exolab.jms.persistence.SQLHelper;
59  
60  
61  /***
62   * This class provides support for creating and destroying tables in RDBMS
63   * databases.
64   *
65   * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
66   * @version $Revision: 1.4 $ $Date: 2005/11/12 12:47:37 $
67   */
68  public class RDBMSTool {
69  
70      /***
71       * The connection to the database.
72       */
73      private Connection _connection = null;
74  
75      /***
76       * The schema browser.
77       */
78      private SchemaBrowser _browser = null;
79  
80      /***
81       * The logger
82       */
83      private static final Log _log = LogFactory.getLog(RDBMSTool.class);
84  
85  
86      /***
87       * Construct a new <code>RDBMSTool</code>.
88       *
89       * @param connection the JDBC connection
90       * @throws PersistenceException if database meta-data can't be obtained
91       */
92      public RDBMSTool(Connection connection) throws PersistenceException {
93          init(connection);
94      }
95  
96      /***
97       * Construct a new <code>RDBMSTool</code>.
98       *
99       * @param config the configuration
100      * @throws PersistenceException for any error
101      */
102     public RDBMSTool(Configuration config) throws PersistenceException {
103         RdbmsDatabaseConfiguration rdbms =
104                 config.getDatabaseConfiguration()
105                 .getRdbmsDatabaseConfiguration();
106         if (rdbms == null) {
107             throw new PersistenceException(
108                     "Configuration not configured to use an RDBMS");
109         }
110         Connection connection = null;
111         try {
112             Class.forName(rdbms.getDriver());
113             connection = DriverManager.getConnection(rdbms.getUrl(),
114                                                       rdbms.getUser(),
115                                                       rdbms.getPassword());
116         } catch (SQLException exception) {
117             throw new PersistenceException(exception);
118         } catch (ClassNotFoundException exception) {
119             throw new PersistenceException(exception);
120         }
121         init(connection);
122     }
123 
124     /***
125      * Determines if a set of tables are present in the dataase.
126      *
127      * @param tables the tables
128      * @return <code>true</code> if all tables are present
129      * @throws PersistenceException for any error
130      */
131     public boolean hasTables(Table[] tables) throws PersistenceException {
132         boolean result = true;
133         for (int i = 0; i < tables.length; ++i) {
134             if (!_browser.getTableExists(tables[i].getName())) {
135                 result = false;
136                 break;
137             }
138         }
139         return result;
140     }
141 
142     /***
143      * Creates the database.
144      *
145      * @param schema the database schema
146      * @throws PersistenceException if elements cannot be created in the
147      *                              database
148      */
149     public void create(Database schema) throws PersistenceException {
150         Table[] tables = schema.getTable();
151         for (int i = 0; i < tables.length; ++i) {
152             create(tables[i]);
153         }
154     }
155 
156     /***
157      * Drops tables from the database.
158      *
159      * @param schema the database schema
160      * @throws PersistenceException if elements cannot be dropped from the
161      *                              database
162      */
163     public void drop(Database schema) throws PersistenceException {
164         Table[] tables = schema.getTable();
165         for (int i = 0; i < tables.length; ++i) {
166             drop(tables[i]);
167         }
168         Deprecated[] redundant = schema.getDeprecated();
169         for (int i = 0; i < redundant.length; ++i) {
170             dropTable(redundant[i].getName());
171         }
172     }
173 
174     /***
175      * Deletes data from all tables in the database.
176      *
177      * @param schema the database schema
178      * @throws PersistenceException if a table can't be truncated
179      */
180     public void delete(Database schema) throws PersistenceException {
181         Table[] tables = schema.getTable();
182         for (int i = 0; i < tables.length; ++i) {
183             deleteTable(tables[i].getName());
184         }
185     }
186 
187     /***
188      * Close the connection to the database.
189      */
190     public void close() {
191         SQLHelper.close(_connection);
192     }
193 
194     /***
195      * Creates a table in the database.
196      *
197      * @param table the table to create
198      * @throws PersistenceException if the table exists, or cannot be created
199      */
200     public void create(Table table) throws PersistenceException {
201         String name = table.getName();
202         if (_browser.getTableExists(name)) {
203             throw new PersistenceException(
204                     "An object already exists in the database named " + name);
205         }
206 
207         StringBuffer sql = new StringBuffer("create table ");
208         sql.append(name);
209         sql.append(" (");
210 
211         _log.debug("Creating table: " + name);
212         Attribute[] attributes = table.getAttribute();
213         for (int i = 0; i < attributes.length; ++i) {
214             if (i > 0) {
215                 sql.append(", ");
216             }
217             Attribute attribute = attributes[i];
218             sql.append(attribute.getName());
219             sql.append(" ");
220             sql.append(getSQLType(attribute));
221             if (attribute.getNotNull()) {
222                 sql.append(" not null");
223             }
224             if (attribute.getPrimaryKey()) {
225                 sql.append(" primary key");
226             }
227             if (attribute.getUnique()) {
228                 sql.append(" unique");
229             }
230         }
231         PrimaryKey key = table.getPrimaryKey();
232         if (key != null) {
233             sql.append(", primary key (");
234             Column[] columns = key.getColumn();
235             for (int i = 0; i < columns.length; ++i) {
236                 if (i > 0) {
237                     sql.append(", ");
238                 }
239                 sql.append(columns[i].getName());
240             }
241             sql.append(")");
242         }
243         sql.append(")");
244 
245         _log.debug("SQL=" + sql);
246         Statement statement = null;
247         try {
248             statement = _connection.createStatement();
249             statement.executeUpdate(sql.toString());
250         } catch (SQLException exception) {
251             throw new PersistenceException("Failed to create table=" + name,
252                                            exception);
253         } finally {
254             SQLHelper.close(statement);
255         }
256         createIndexes(table);
257     }
258 
259     /***
260      * Drops a table from the database. If the table doesn't exist, then
261      * it will be ignored.
262      *
263      * @param table the table to drop
264      * @throws PersistenceException for any database error
265      */
266     public void drop(Table table) throws PersistenceException {
267         dropTable(table.getName());
268     }
269 
270     /***
271      * Returns the schema browser.
272      *
273      * @return the schema browser
274      */
275     public SchemaBrowser getSchemaBrowser() {
276         return _browser;
277     }
278 
279     /***
280      * Initialise this.
281      *
282      * @param connection the connection to use
283      * @throws PersistenceException for any error
284      *
285       */
286     private void init(Connection connection) throws PersistenceException {
287         _connection = connection;
288         try {
289             _connection.setAutoCommit(true);
290         } catch (SQLException exception) {
291             throw new PersistenceException("Failed to set auto-commit on",
292                                            exception);
293         }
294         _browser = new SchemaBrowser(_connection);
295     }
296 
297     /***
298      * Create indexes for a table.
299      *
300      * @param table the table to add indexes for
301      * @throws PersistenceException
302      */
303     private void createIndexes(Table table) throws PersistenceException {
304         Index[] indexes = table.getIndex();
305         for (int i = 0; i < indexes.length; ++i) {
306             Index index = indexes[i];
307             StringBuffer sql = new StringBuffer("create ");
308             if (index.getUnique()) {
309                 sql.append("unique ");
310             }
311             sql.append("index ");
312             sql.append(index.getName());
313             sql.append(" on ");
314             sql.append(table.getName());
315             sql.append("(");
316             Column[] columns = index.getColumn();
317             for (int j = 0; j < columns.length; ++j) {
318                 if (j > 0) {
319                     sql.append(", ");
320                 }
321                 sql.append(columns[j].getName());
322             }
323             sql.append(")");
324             _log.debug("SQL=" + sql);
325             Statement statement = null;
326             try {
327                 statement = _connection.createStatement();
328                 statement.executeUpdate(sql.toString());
329             } catch (SQLException exception) {
330                 throw new PersistenceException("Failed to create index="
331                                                + index.getName()
332                                                + " on table "
333                                                + table.getName(), exception);
334             } finally {
335                 SQLHelper.close(statement);
336             }
337         }
338     }
339 
340     /***
341      * Drop a table.
342      *
343      * @param name the name of the table to drop
344      * @throws PersistenceException if the drop fails
345      */
346     private void dropTable(String name) throws PersistenceException {
347         if (_browser.getTableExists(name)) {
348             String sql = "drop table " + name;
349             _log.debug("SQL=" + sql);
350             Statement statement = null;
351             try {
352                 statement = _connection.createStatement();
353                 statement.executeUpdate(sql);
354             } catch (SQLException exception) {
355                 throw new PersistenceException("Failed to drop table=" + name,
356                                                exception);
357             } finally {
358                 SQLHelper.close(statement);
359             }
360         }
361     }
362 
363     /***
364      * Deletes all data in a table.
365      *
366      * @param name the name of the table to delete from
367      * @throws PersistenceException if the delete fails
368      */
369     private void deleteTable(String name) throws PersistenceException {
370         if (_browser.getTableExists(name)) {
371             String sql = "delete from " + name;
372             _log.debug("SQL=" + sql);
373             Statement statement = null;
374             try {
375                 statement = _connection.createStatement();
376                 statement.execute(sql);
377             } catch (SQLException exception) {
378                 throw new PersistenceException("Failed to delete from table="
379                                                + name, exception);
380             } finally {
381                 SQLHelper.close(statement);
382             }
383         }
384     }
385 
386     /***
387      * Returns the SQL type for a given attribute.
388      *
389      * @param attribute the attribute
390      * @return a string representation of the type
391      * @throws PersistenceException if {@link Attribute#getType} is invalid, or
392      *                              the RDBMS doesn't support the type
393      */
394     private String getSQLType(Attribute attribute)
395             throws PersistenceException {
396         Type result = _browser.getType(attribute);
397         _log.debug("attribute=" + attribute.getName() + "->" + result);
398         return result.getSQL();
399     }
400 
401 }