View Javadoc

1   /**
2    * Copyright 2006-2012 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.torque.mojo;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.commons.io.FileUtils;
25  import org.apache.commons.io.IOUtils;
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.maven.plugin.AbstractMojo;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.torque.util.SimpleScanner;
30  
31  /**
32   * Examine SQL files and convert the delimiter style from using a <code>;</code> at the end of the line to a <code>/</code> on it's own line
33   *
34   * @goal convertsql
35   */
36  public class ConvertSQLMojo extends AbstractMojo {
37  	private static final String FS = File.separator;
38  
39  	/**
40  	 * The encoding to use when reading/writing files
41  	 *
42  	 * @parameter expression="${impex.encoding}" default-value="${project.build.sourceEncoding}"
43  	 */
44  	private String encoding;
45  
46  	/**
47  	 * The old delimiter used to terminate a SQL statement by being present at the end of a line.
48  	 *
49  	 * @parameter expression="${impex.oldDelimiter}" default-value=";"
50  	 * @required
51  	 */
52  	private String oldDelimiter;
53  
54  	/**
55  	 * The new delimiter to put on its own line
56  	 *
57  	 * @parameter expression="${impex.newDelimiter}" default-value="/"
58  	 * @required
59  	 */
60  	private String newDelimiter;
61  
62  	/**
63  	 * Director to examine for .sql files
64  	 *
65  	 * @parameter expression="${impex.sourceDir}" default-value="${project.build.directory}/sql/source"
66  	 * @required
67  	 */
68  	private File sourceDir;
69  
70  	/**
71  	 * Directory to generate the converted files into
72  	 *
73  	 * @parameter expression="${impex.outputDir}" default-value="${project.build.directory}/sql/output"
74  	 * @required
75  	 */
76  	private File outputDir;
77  
78  	/**
79  	 * Files to include
80  	 *
81  	 * @parameter expression="${impex.includes}" default-value="*.sql"
82  	 */
83  	private String includes;
84  
85  	/**
86  	 * Files to exclude
87  	 *
88  	 * @parameter expression="${impex.excludes}"
89  	 */
90  	private String excludes;
91  
92  	/**
93  	 * Liquibase injects a handful of metadata at the top of each SQL file that causes "noise". All of the checksums and actual SQL may stay
94  	 * the same, but the Liquibase metadata makes it appear as though the resulting SQL file has changed. By omitting the metadata you can
95  	 * run the same change set multiple times and get the exact same SQL file as output. This makes it much easier to perform validation
96  	 * checks on the SQL.
97  	 *
98  	 * @parameter expression="${impex.skipIrrelevantLiquibaseMetadataLines}" default-value="false"
99  	 */
100 	private boolean skipIrrelevantLiquibaseMetadataLines;
101 
102 	@Override
103 	public void execute() throws MojoExecutionException {
104 		try {
105 			List<File> files = getFiles();
106 			if (files == null || files.size() == 0) {
107 				getLog().info("No files found");
108 				return;
109 			}
110 			getLog().info("Found " + files.size() + " SQL files to convert");
111 			convert(files);
112 		} catch (Exception e) {
113 			throw new MojoExecutionException("Unexpected error", e);
114 		}
115 	}
116 
117 	protected void convert(List<File> files) throws IOException {
118 		for (File file : files) {
119 			convert(file);
120 		}
121 	}
122 
123 	protected List<String> getLiquibaseTokens() {
124 		List<String> tokens = new ArrayList<String>();
125 		tokens.add("-- Ran at:");
126 		tokens.add("-- Against:");
127 		tokens.add("-- Liquibase version:");
128 		return tokens;
129 	}
130 
131 	protected boolean isSkipLine(String line) {
132 		if (!skipIrrelevantLiquibaseMetadataLines) {
133 			return false;
134 		}
135 		List<String> tokens = getLiquibaseTokens();
136 		for (String token : tokens) {
137 			if (line.startsWith(token)) {
138 				return true;
139 			}
140 		}
141 		return false;
142 	}
143 
144 	protected void convert(File file) throws IOException {
145 		String outputFilename = outputDir + FS + file.getName();
146 		File outputFile = new File(outputFilename);
147 		@SuppressWarnings("unchecked")
148 		List<String> lines = FileUtils.readLines(file, encoding);
149 		getLog().info("Creating " + outputFile.getCanonicalPath());
150 		OutputStream out = null;
151 		try {
152 			out = FileUtils.openOutputStream(outputFile);
153 			for (String line : lines) {
154 				if (isSkipLine(line)) {
155 					continue;
156 				}
157 				String convertedLine = getConvertedLine(line);
158 				out.write(convertedLine.getBytes(encoding));
159 			}
160 		} finally {
161 			IOUtils.closeQuietly(out);
162 		}
163 	}
164 
165 	protected String getConvertedLine(String line) {
166 		String trimmed = StringUtils.trim(line);
167 		if (trimmed.endsWith(oldDelimiter)) {
168 			int pos = line.lastIndexOf(oldDelimiter);
169 			return line.substring(0, pos) + IOUtils.LINE_SEPARATOR_UNIX + newDelimiter + IOUtils.LINE_SEPARATOR_UNIX;
170 		} else {
171 			return line + IOUtils.LINE_SEPARATOR_UNIX;
172 		}
173 	}
174 
175 	protected List<File> getFiles() throws IOException {
176 		FileUtils.forceMkdir(sourceDir);
177 		getLog().info("Examining " + sourceDir.getCanonicalPath());
178 		SimpleScanner scanner = new SimpleScanner(sourceDir, includes, excludes);
179 		return scanner.getFiles();
180 	}
181 
182 	public File getSourceDir() {
183 		return sourceDir;
184 	}
185 
186 	public void setSourceDir(File inputDir) {
187 		this.sourceDir = inputDir;
188 	}
189 
190 	public File getOutputDir() {
191 		return outputDir;
192 	}
193 
194 	public void setOutputDir(File outputDir) {
195 		this.outputDir = outputDir;
196 	}
197 
198 	public String getIncludes() {
199 		return includes;
200 	}
201 
202 	public void setIncludes(String includes) {
203 		this.includes = includes;
204 	}
205 
206 	public String getExcludes() {
207 		return excludes;
208 	}
209 
210 	public void setExcludes(String excludes) {
211 		this.excludes = excludes;
212 	}
213 
214 	public String getEncoding() {
215 		return encoding;
216 	}
217 
218 	public void setEncoding(String encoding) {
219 		this.encoding = encoding;
220 	}
221 
222 	public String getOldDelimiter() {
223 		return oldDelimiter;
224 	}
225 
226 	public void setOldDelimiter(String oldDelimiter) {
227 		this.oldDelimiter = oldDelimiter;
228 	}
229 
230 	public String getNewDelimiter() {
231 		return newDelimiter;
232 	}
233 
234 	public void setNewDelimiter(String newDelimiter) {
235 		this.newDelimiter = newDelimiter;
236 	}
237 
238 	public boolean isSkipIrrelevantLiquibaseMetadataLines() {
239 		return skipIrrelevantLiquibaseMetadataLines;
240 	}
241 
242 	public void setSkipIrrelevantLiquibaseMetadataLines(boolean skipIrrelevantLiquibaseMetadataLines) {
243 		this.skipIrrelevantLiquibaseMetadataLines = skipIrrelevantLiquibaseMetadataLines;
244 	}
245 
246 }