View Javadoc

1   /*
2    * #%L
3    * Bitrepository Core
4    * %%
5    * Copyright (C) 2010 - 2012 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.io.IOException;
25  import java.io.LineNumberReader;
26  import java.io.Reader;
27  import java.sql.Connection;
28  import java.sql.ResultSet;
29  import java.sql.ResultSetMetaData;
30  import java.sql.SQLException;
31  import java.sql.Statement;
32  
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * Runs a sql script as a sequence of JDBC statements.
38   *
39   * Slightly modified version of the com.ibatis.common.jdbc.SqlScriptRunner class
40   * from the iBATIS Apache project. Only removed dependency on Resource class
41   * and a constructor
42   */
43  public class SqlScriptRunner {
44      private static final String DEFAULT_DELIMITER = ";";
45      private static Logger log = LoggerFactory.getLogger(SqlScriptRunner.class);
46  
47      private Connection connection;
48  
49      private boolean stopOnError;
50      private boolean autoCommit;
51  
52      private String delimiter = DEFAULT_DELIMITER;
53      private boolean fullLineDelimiter = false;
54  
55      /**
56       *
57       * @param connection The connection to use
58       * @param autoCommit Enable autocommit
59       * @param stopOnError Stop running the script, if a statement fails.
60       */
61      public SqlScriptRunner(Connection connection,
62                             boolean autoCommit,
63                             boolean stopOnError) {
64          this.connection = connection;
65          this.autoCommit = autoCommit;
66          this.stopOnError = stopOnError;
67      }
68  
69      /**
70       * @param delimiter The statement delimiter, eg. ';' for mysql.
71       * @param fullLineDelimiter <code>true</code> if the delimiter used to distinguise between lines.
72       */
73      public void setDelimiter(String delimiter, boolean fullLineDelimiter) {
74          this.delimiter = delimiter;
75          this.fullLineDelimiter = fullLineDelimiter;
76      }
77  
78      /**
79       * Runs an SQL script (read in using the Reader parameter)
80       *
81       * @param reader
82       *            - the source of the script
83       */
84      public void runScript(Reader reader) throws IOException, SQLException {
85          try {
86              boolean originalAutoCommit = connection.getAutoCommit();
87              if (originalAutoCommit != this.autoCommit) {
88                  connection.setAutoCommit(this.autoCommit);
89              }
90              runScript(connection, reader);
91              connection.setAutoCommit(originalAutoCommit);
92          } catch (Exception e) {
93              throw new RuntimeException("Error running script.  Cause: " + e, e);
94          }
95      }
96  
97      /**
98       * Runs an SQL script (read in using the Reader parameter) using the
99       * connection passed in
100      *
101      * @param conn the connection to use for the script.
102      * @param reader the source of the script.
103      * @throws SQLException if any SQL errors occur.
104      * @throws IOException if there is an error reading from the Reader.
105      */
106     private void runScript(Connection conn, Reader reader) throws IOException,
107             SQLException {
108         StringBuffer command = null;
109         try {
110             LineNumberReader lineReader = new LineNumberReader(reader);
111             String line = null;
112             while ((line = lineReader.readLine()) != null) {
113                 if (command == null) {
114                     command = new StringBuffer();
115                 }
116                 String trimmedLine = line.trim();
117                 if (trimmedLine.startsWith("--") ||
118                         trimmedLine.startsWith("//")) {
119                     // Ignore comment line
120                 } else if (trimmedLine.length() == 0) {
121                     // Ignore empty line.
122                 } else if (trimmedLine.contains("connect")) {
123                     // Ignore connect statement as this is handled in the supplied connection.
124                 } else if (!fullLineDelimiter
125                         && trimmedLine.endsWith(getDelimiter())
126                         || fullLineDelimiter
127                         && trimmedLine.equals(getDelimiter())) {
128 
129                     command.append(trimLineForComment(line).substring(0, line
130                             .lastIndexOf(getDelimiter())));
131                     command.append(" ");
132 
133                     Statement statement = conn.createStatement();
134 
135                     log.debug("Executing statement: " + command.toString());
136 
137                     boolean hasResults = false;
138                     if (stopOnError) {
139                         hasResults = statement.execute(command.toString());
140                     } else {
141                         try {
142                             statement.execute(command.toString());
143                         } catch (SQLException e) {
144                             e.fillInStackTrace();
145                             log.error("Error executing: " + command, e);
146                         }
147                     }
148 
149                     if (autoCommit && !conn.getAutoCommit()) {
150                         conn.commit();
151                     }
152 
153                     ResultSet rs = statement.getResultSet();
154                     StringBuilder resultSB = new StringBuilder();
155                     if (hasResults && rs != null) {
156                         ResultSetMetaData md = rs.getMetaData();
157                         int cols = md.getColumnCount();
158                         for (int i = 0; i < cols; i++) {
159                             String name = md.getColumnLabel(i);
160                             resultSB.append(name + "\t");
161                         }
162                         resultSB.append("\n");
163                         while (rs.next()) {
164                             for (int i = 0; i < cols; i++) {
165                                 String value = rs.getString(i);
166                                 resultSB.append(value + "\t");
167                             }
168                             resultSB.append("\n");
169                         }
170                         log.info("Result: " + resultSB.toString());
171                     }
172 
173                     command = null;
174                     try {
175                         statement.close();
176                     } catch (Exception e) {
177                         // Ignore to workaround a bug in Jakarta DBCP
178                     }
179                     Thread.yield();
180                 } else {
181                     command.append(trimLineForComment(line));
182                     command.append(" ");
183                 }
184             }
185             if (!autoCommit) {
186                 conn.commit();
187             }
188         } catch (SQLException e) {
189             throw e;
190         } catch (IOException e) {
191             throw e;
192         }
193     }
194 
195     private String trimLineForComment(String line) {
196         if (line.contains("--")) {
197             return line.substring(0, line.indexOf("--"));
198         } else {
199             return line;
200         }
201     }
202 
203     private String getDelimiter() {
204         return delimiter;
205     }
206 }