package org.jstryker.database; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.nio.charset.Charset; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.jstryker.exception.JStrykerException; /** * Tool to run database scripts. */ public class ScriptRunner { /** * Default statement delimiter is ';'. */ public static final String DEFAULT_DELIMITER = ";"; private Connection connection; private String delimiter; private Charset charset; /** * Create new {@link ScriptRunner} with {@link Connection}, statement delimiter and {@link Charset}. * @param connection {@link Connection} to run script. * @param delimiter Statement delimiter eg: ';' or '$$'. * @param charset {@link Charset} to read stream. */ public ScriptRunner(Connection connection, String delimiter, Charset charset) { this.connection = connection; this.delimiter = delimiter; this.charset = charset; } /** * Create new {@link ScriptRunner} with {@link Connection}, default statement delimiter and default {@link Charset}. * @see {@link ScriptRunner#DEFAULT_DELIMITER} * @param connection {@link Connection} to run script. */ public ScriptRunner(Connection connection) { this(connection, DEFAULT_DELIMITER, Charset.defaultCharset()); } /** * Create new {@link ScriptRunner} with {@link Connection}, default statement delimiter and {@link Charset}. * @param connection {@link Connection} to run script. * @param charset {@link Charset} to read stream. */ public ScriptRunner(Connection connection, Charset charset) { this(connection, DEFAULT_DELIMITER, charset); } /** * Create new {@link ScriptRunner} with {@link Connection}, default {@link Charset} and statement delimiter. * @param connection {@link Connection} to run script. * @param delimiter Statement delimiter eg: ';' or '$$'. */ public ScriptRunner(Connection connection, String delimiter) { this(connection, delimiter, Charset.defaultCharset()); } /** * Run an SQL script. * @param stream The source of the script. * @throws JStrykerException When cannot execute script. * @throws IllegalArgumentException If stream is null. */ public void runScript(InputStream stream) throws JStrykerException, IllegalArgumentException { if (stream == null) { throw new IllegalArgumentException("Stream cannot be null."); } try { List<String> commands = parse(stream); for (String command : commands) { Statement statement = null; try { statement = connection.createStatement(); statement.execute(command.toString()); } finally { if (statement != null) { statement.close(); } } } } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } catch (IOException e) { throw new JStrykerException(e.getMessage(), e); } } /** * @param stream The source of the script. * @return List of commands to be executed. * @throws IOException When cannot read stream. */ private List<String> parse(InputStream stream) throws IOException { InputStreamReader reader = new InputStreamReader(stream, charset); LineNumberReader lineReader = new LineNumberReader(reader); List<String> commands = new ArrayList<String>(); String line = null; StringBuilder command = new StringBuilder(); while ((line = lineReader.readLine()) != null) { String trimmedLine = line.trim(); if (trimmedLine.startsWith("--")) { continue; } else if (trimmedLine.endsWith(delimiter)) { command.append(line.substring(0, line.lastIndexOf(delimiter))); commands.add(command.toString()); command = new StringBuilder(); } else { command.append(line); command.append(" "); } } return commands; } }