com.tacitknowledge.util.migration.jdbc.SqlScriptMigrationTaskSource Maven / Gradle / Ivy
/* Copyright 2004 Tacit Knowledge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tacitknowledge.util.migration.jdbc;
import com.tacitknowledge.util.discovery.ClassDiscoveryUtil;
import com.tacitknowledge.util.migration.MigrationException;
import com.tacitknowledge.util.migration.MigrationTask;
import com.tacitknowledge.util.migration.MigrationTaskSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Search a package (directory) for SQL scripts that a specific pattern and
* returns corresponding SqlScriptMigrationTasks. The name of
* each script must follow the pattern of "patch(\d+)(_.+)?\.sql".
*
* @author Scott Askew ([email protected])
*/
public class SqlScriptMigrationTaskSource implements MigrationTaskSource
{
/**
* Class logger
*/
private static Log log = LogFactory
.getLog(SqlScriptMigrationTaskSource.class);
/**
* The regular expression used to match SQL patch files.
*/
private static final String SQL_PATCH_REGEX = "^patch(\\d++)(?!-rollback)_?(.+)?\\.sql";
/**
* The regular expression used to match SQL rollback files
*/
private static final String SQL_ROLLBACK_REGEX = "^patch(\\d++)-rollback_?(.+)?\\.sql";
/**
* {@inheritDoc}
*/
public List getMigrationTasks(String packageName) throws MigrationException
{
String path = packageName.replace('.', '/');
String[] upScripts = getSqlScripts(path, SQL_PATCH_REGEX);
String[] downScripts = getSqlScripts(path, SQL_ROLLBACK_REGEX);
return createMigrationScripts(upScripts, downScripts);
}
/**
* Returns the SQL scripts which exist in the specified patch and match the specified regex.
*
* @param path the Path to search
* @param regex the regex which the scripts should match
* @return a String array of script names.
*/
private String[] getSqlScripts(String path, String regex)
{
String[] scripts = ClassDiscoveryUtil.getResources(path, regex);
if (log.isDebugEnabled())
{
log.debug("Found " + scripts.length + " patches in path: " + path);
for (int i = 0; i < scripts.length; i++)
{
log.debug(" -- \"" + scripts[i] + "\"");
}
}
return scripts;
}
/**
* Creates a list of SqlScriptMigrationTasks based on the
* array of SQL scripts.
*
* @param upScripts the classpath-relative array of SQL migration scripts
* @param downScripts the classpath-relative array of SQL migration scripts
* @return a list of SqlScriptMigrationTasks based on the
* array of SQL scripts
* @throws MigrationException if a SqlScriptMigrationTask could no be created
*/
private List createMigrationScripts(String[] upScripts, String[] downScripts)
throws MigrationException
{
Pattern upFileNamePattern = Pattern.compile(SQL_PATCH_REGEX);
Pattern downFileNamePattern = Pattern.compile(SQL_ROLLBACK_REGEX);
List tasks = new ArrayList();
for (int i = 0; i < upScripts.length; i++)
{
String script = upScripts[i];
if (script != null)
{
// get the file name
File scriptFile = new File(script);
String scriptFileName = scriptFile.getName();
// Get the version out of the script name
int order = getOrder(upFileNamePattern, script, scriptFileName);
// get the down script which matches this patch order
String downScript = getMatchingDownScript(downScripts, order,
downFileNamePattern);
// read the scripts
String upSql = readSql(getInputStream(script));
String downSql = "";
// if the down script is not the empty string, then try to read
// it.
if (!"".equals(downScript))
downSql = readSql(getInputStream(downScript));
// create a new task
SqlScriptMigrationTask task = new SqlScriptMigrationTask(
scriptFileName, order, upSql, downSql);
task.setName(scriptFileName);
// add the task to the list of tasks
tasks.add(task);
}
}
return tasks;
}
/**
* Returns the filename of the downscript which matches the order of the order parameter.
*
* @param downScripts an array of scripts
* @param order the order of the down script whose name should be returned
* @return the name of the down script that matches the order
*/
private String getMatchingDownScript(String[] downScripts, int order,
Pattern fileNamePattern) throws MigrationException
{
boolean isScriptFound = false;
String script = "";
for (int i = 0; i < downScripts.length && !isScriptFound; i++)
{
File scriptFile = new File(downScripts[i]);
String scriptFileName = scriptFile.getName();
int downScriptOrder = getOrder(fileNamePattern, downScripts[i],
scriptFileName);
if (downScriptOrder == order)
{
script = downScripts[i];
isScriptFound = true;
}
}
if (!isScriptFound)
{
log.info("There was no rollback script for patch level: " + order);
}
return script;
}
/**
* Returns an input stream that points to the script name
*
* @param scriptName the name of the script to create an InputStream
* @return an InputStream returns an InputStream based upon the scriptName
*/
private InputStream getInputStream(String scriptName)
{
scriptName = scriptName.replace('\\', '/');
log.debug("Examining possible SQL patch file \"" + scriptName + "\"");
return Thread.currentThread().getContextClassLoader()
.getResourceAsStream(scriptName);
}
/**
* Reads the file contents into a String object.
*
* @param is the InputStream
* @return a String with the contents of the InputStream
* @throws MigrationException if there's an error reading in the contents
*/
private String readSql(InputStream is) throws MigrationException
{
StringBuffer sqlBuffer = new StringBuffer();
BufferedReader buf = new BufferedReader(new InputStreamReader(is));
try
{
String line = buf.readLine();
while (line != null)
{
sqlBuffer.append(line).append("\n");
line = buf.readLine();
}
}
catch (IOException ioe)
{
throw new MigrationException(
"There was an error reading in a script", ioe);
}
finally
{
try
{
is.close();
}
catch (IOException ioe)
{
log.error("Could not close input stream", ioe);
}
}
return sqlBuffer.toString();
}
/**
* Returns the order for the file.
*
* @param p a Pattern defining the file name pattern
* @param script the Script
* @param scriptFileName the name of the file
* @return an int indicating the order
* @throws MigrationException in case the file name is invalid
*/
private int getOrder(Pattern p, String script, String scriptFileName)
throws MigrationException
{
Matcher matcher = p.matcher(scriptFileName);
if (!matcher.matches() || matcher.groupCount() != 2)
{
throw new MigrationException("Invalid SQL script name: " + script);
}
int order = Integer.parseInt(matcher.group(1));
return order;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy