View Javadoc

1   /*
2    * #%L
3    * Bitrepository Service
4    * %%
5    * Copyright (C) 2010 - 2013 The State and University Library, The Royal Library and The State Archives, Denmark
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 2.1 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-2.1.html>.
20   * #L%
21   */
22  package org.bitrepository.service.database;
23  
24  import java.sql.Connection;
25  import java.sql.PreparedStatement;
26  import java.sql.ResultSet;
27  import java.sql.SQLException;
28  import java.util.HashMap;
29  import java.util.Map;
30  
31  /**
32   * Abstract class for migrating the databases.
33   * Handles the default operations.
34   * 
35   * It is asserted, that the database has the following table for the versions:
36   *  
37   * create table tableversions (
38   *   tablename varchar(100) not null, -- Name of table
39   *   version int not null             -- version of table
40   * );
41   */
42  public abstract class DatabaseMigrator extends DatabaseMaintainer {
43      /** The connection to the database.*/
44      protected final DBConnector connector;
45      
46      /** The name of the "table versions" table.*/
47      protected static final String TABLEVERSIONS_TABLE = "tableversions";
48      /** The 'tablename' coloumn in the table.*/
49      protected static final String TV_TABLENAME = "tablename";
50      /** The 'version' coloumn in the table.*/
51      protected static final String TV_VERSION = "version";
52      
53      /**
54       * Constructor.
55       * @param connector The connector for the database.
56       */
57      protected DatabaseMigrator(DBConnector connector) {
58          this.connector = connector;
59      }
60      
61      /**
62       * Extracts the version numbers for the tables in the database.
63       * @return The mapping between the table names and their respective version number.
64       */
65      protected Map<String, Integer> getTableVersions() {
66          Map<String, Integer> resultMap = new HashMap<String, Integer>();
67          
68          // Extract the table name as first coloumn and version as second coloumn. 
69          String sql = "SELECT " + TV_TABLENAME + " , " + TV_VERSION + " FROM " + TABLEVERSIONS_TABLE;
70          int tablenameColoumn = 1;
71          int versionColoumn = 2;
72          
73          try {
74              PreparedStatement ps = null;
75              ResultSet res = null;
76              Connection conn = null;
77              try {
78                  conn = connector.getConnection();
79                  ps = DatabaseUtils.createPreparedStatement(conn, sql, new Object[0]);
80                  res = ps.executeQuery();
81                  
82                  while (res.next()) {
83                      resultMap.put(res.getString(tablenameColoumn), res.getInt(versionColoumn));
84                  }
85              } finally {
86                  if(res != null) {
87                      res.close();
88                  }
89                  if(ps != null) {
90                      ps.close();
91                  }
92                  if(conn != null) {
93                      conn.close();
94                  }
95              }
96          } catch (SQLException e) {
97              throw new IllegalStateException("Cannot extract the table versions.", e);
98          }
99          
100         return resultMap;
101     }
102     
103     /**
104      * Performs the update of a single table.
105      * @param tablename The name of the table to update.
106      * @param newVersion The new version for the table.
107      * @param updateSql The SQL for updating the table.
108      * @param args The arguments for performing this update.
109      */
110     protected void updateTable(String tablename, Integer newVersion, String updateSql, Object ... args) {
111         String migrateSql = "UPDATE " + TABLEVERSIONS_TABLE + " SET " + TV_VERSION + " = ? WHERE " + TV_TABLENAME 
112                 + " = ?";
113         
114         DatabaseUtils.executeStatement(connector, updateSql, args);
115         DatabaseUtils.executeStatement(connector, migrateSql, newVersion, tablename);
116     }
117     
118     /**
119      * Method for running a specific migrate script on the embedded database.
120      * Will throw an exception if the migration is tried to be performed on another type of database.
121      * @param migrateScriptName The name of the migrate script to run.
122      */
123     protected void migrateDerbyDatabase(String migrateScriptName) {
124         if(connector.getDatabaseDriverClass().equals(DatabaseUtils.DERBY_EMBEDDED_DRIVER)) {
125             try {
126                 runScript(connector, migrateScriptName);
127             } catch (Exception e) {
128                 throw new IllegalStateException("Cannot migrate the database with the script '" + migrateScriptName 
129                         + "'. It is very possible that the database has to be migrated by manually running "
130                         + "migrate-scripts.", e);
131             }
132         } else {
133             throw new IllegalStateException("Can only perform database migrations on embedded derby databases. "
134                     + "Migration of other databases must be performed manually. Your database had the drivers: "
135                     + connector.getDatabaseDriverClass());
136         }
137     }
138     
139     /**
140      * Perform the migration for the given database.
141      */
142     abstract public void migrate();
143     
144     /**
145      * Method to determine if migration is needed.
146      * @return true if migration is needed, false otherwise. 
147      */
148     public abstract boolean needsMigration();
149     
150 }