View Javadoc

1   package org.codehaus.mojo.sql;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedOutputStream;
23  import java.io.BufferedReader;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileOutputStream;
27  import java.io.FileReader;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.InputStreamReader;
31  import java.io.OutputStream;
32  import java.io.PrintStream;
33  import java.io.Reader;
34  import java.io.StringReader;
35  import java.sql.Connection;
36  import java.sql.Driver;
37  import java.sql.ResultSet;
38  import java.sql.ResultSetMetaData;
39  import java.sql.SQLException;
40  import java.sql.SQLWarning;
41  import java.sql.Statement;
42  import java.util.Collections;
43  import java.util.Enumeration;
44  import java.util.Properties;
45  import java.util.StringTokenizer;
46  import java.util.Vector;
47  
48  import org.apache.commons.io.IOUtils;
49  import org.apache.commons.lang.StringEscapeUtils;
50  import org.apache.maven.execution.MavenSession;
51  import org.apache.maven.plugin.AbstractMojo;
52  import org.apache.maven.plugin.MojoExecutionException;
53  import org.apache.maven.project.MavenProject;
54  import org.apache.maven.settings.Server;
55  import org.apache.maven.settings.Settings;
56  import org.apache.maven.shared.filtering.MavenFileFilter;
57  import org.apache.maven.shared.filtering.MavenFileFilterRequest;
58  import org.apache.maven.shared.filtering.MavenFilteringException;
59  import org.codehaus.plexus.util.FileUtils;
60  import org.codehaus.plexus.util.IOUtil;
61  import org.codehaus.plexus.util.StringUtils;
62  import org.springframework.core.io.DefaultResourceLoader;
63  import org.springframework.core.io.Resource;
64  import org.springframework.core.io.ResourceLoader;
65  
66  /**
67   * Executes SQL against a database.
68   * 
69   * @goal execute
70   */
71  public class SqlExecMojo extends AbstractMojo {
72  
73  	/**
74  	 * Call {@link #setOnError(String)} with this value to abort SQL command
75  	 * execution if an error is found.
76  	 */
77  	public static final String ON_ERROR_ABORT = "abort";
78  
79  	/**
80  	 * Call {@link #setOnError(String)} with this value to continue SQL command
81  	 * execution until all commands have been attempted, then abort the build if
82  	 * an SQL error occurred in any of the commands.
83  	 */
84  	public static final String ON_ERROR_ABORT_AFTER = "abortAfter";
85  
86  	/**
87  	 * Call {@link #setOnError(String)} with this value to continue SQL command
88  	 * execution if an error is found.
89  	 */
90  	public static final String ON_ERROR_CONTINUE = "continue";
91  
92  	/**
93  	 * Call {@link #setOrderFile(String)} with this value to sort in ascendant
94  	 * order the sql files.
95  	 */
96  	public static final String FILE_SORTING_ASC = "ascending";
97  
98  	/**
99  	 * Call {@link #setOrderFile(String)} with this value to sort in descendant
100 	 * order the sql files.
101 	 */
102 	public static final String FILE_SORTING_DSC = "descending";
103 
104 	// ////////////////////////// User Info ///////////////////////////////////
105 
106 	/**
107 	 * Database username. If not given, it will be looked up through
108 	 * <code>settings.xml</code>'s server with <code>${settingsKey}</code> as
109 	 * key.
110 	 * 
111 	 * @since 1.0
112 	 * @parameter expression="${username}"
113 	 */
114 	private String username;
115 
116 	/**
117 	 * Database password. If not given, it will be looked up through
118 	 * <code>settings.xml</code>'s server with <code>${settingsKey}</code> as
119 	 * key.
120 	 * 
121 	 * @since 1.0
122 	 * @parameter expression="${password}"
123 	 */
124 	private String password;
125 
126 	/**
127 	 * Ignore the password and use anonymous access. This may be useful for
128 	 * databases like MySQL which do not allow empty password parameters in the
129 	 * connection initialization.
130 	 * 
131 	 * @since 1.4
132 	 * @parameter default-value="false"
133 	 */
134 	private boolean enableAnonymousPassword;
135 
136 	/**
137 	 * Additional key=value pairs separated by comma to be passed into JDBC
138 	 * driver.
139 	 * 
140 	 * @since 1.0
141 	 * @parameter expression="${driverProperties}" default-value = ""
142 	 */
143 	private String driverProperties;
144 
145 	/**
146 	 * @parameter expression="${settings}"
147 	 * @required
148 	 * @since 1.0
149 	 * @readonly
150 	 */
151 	private Settings settings;
152 
153 	/**
154 	 * Server's <code>id</code> in <code>settings.xml</code> to look up username
155 	 * and password. Defaults to <code>${url}</code> if not given.
156 	 * 
157 	 * @since 1.0
158 	 * @parameter expression="${settingsKey}"
159 	 */
160 	private String settingsKey;
161 
162 	/**
163 	 * Skip execution when there is an error obtaining a connection. This is a
164 	 * special case to support databases, such as embedded Derby, that can
165 	 * shutdown the database via the URL (i.e. <code>shutdown=true</code>).
166 	 * 
167 	 * @since 1.1
168 	 * @parameter expression="${skipOnConnectionError}" default-value="false"
169 	 */
170 	private boolean skipOnConnectionError;
171 
172 	/**
173 	 * Setting this parameter to <code>true</code> will force the execution of
174 	 * this mojo, even if it would get skipped usually.
175 	 * 
176 	 * @parameter expression="${forceOpenJpaExecution}" default-value=false
177 	 * @required
178 	 */
179 	private boolean forceMojoExecution;
180 
181 	/**
182 	 * The Maven Project Object
183 	 * 
184 	 * @parameter default-value="${project}"
185 	 * @required
186 	 * @readonly
187 	 */
188 	protected MavenProject project;
189 
190 	/**
191 	 * @parameter default-value="${session}"
192 	 * @required
193 	 * @readonly
194 	 */
195 	private MavenSession mavenSession;
196 
197 	// ////////////////////////////// Source info /////////////////////////////
198 
199 	/**
200 	 * SQL input commands separated by <code>${delimiter}</code>.
201 	 * 
202 	 * @since 1.0
203 	 * @parameter expression="${sqlCommand}" default-value=""
204 	 */
205 	private String sqlCommand = "";
206 
207 	/**
208 	 * List of files containing SQL statements to load.
209 	 * 
210 	 * @since 1.0
211 	 * @parameter
212 	 */
213 	private File[] srcFiles;
214 
215 	/**
216 	 * List of resources containing SQL statements
217 	 * 
218 	 * @since 1.5
219 	 * @parameter
220 	 */
221 	private String[] resourceLocations;
222 
223 	/**
224 	 * File(s) containing SQL statements to load.
225 	 * 
226 	 * @since 1.0
227 	 * @parameter
228 	 */
229 	private Fileset fileset;
230 
231 	/**
232 	 * When <code>true</code>, skip the execution.
233 	 * 
234 	 * @since 1.0
235 	 * @parameter default-value="false"
236 	 */
237 	private boolean skip;
238 
239 	// //////////////////////////////// Database info /////////////////////////
240 	/**
241 	 * Database URL.
242 	 * 
243 	 * @parameter expression="${url}"
244 	 * @required
245 	 * @since 1.0-beta-1
246 	 */
247 	private String url;
248 
249 	/**
250 	 * Database driver classname.
251 	 * 
252 	 * @since 1.0
253 	 * @parameter expression="${driver}"
254 	 * @required
255 	 */
256 	private String driver;
257 
258 	// //////////////////////////// Operation Configuration ////////////////////
259 	/**
260 	 * Set to <code>true</code> to execute none-transactional SQL.
261 	 * 
262 	 * @since 1.0
263 	 * @parameter expression="${autocommit}" default-value="false"
264 	 */
265 	private boolean autocommit;
266 
267 	/**
268 	 * Action to perform if an error is found. Possible values are
269 	 * <code>abort</code> and <code>continue</code>.
270 	 * 
271 	 * @since 1.0
272 	 * @parameter expression="${onError}" default-value="abort"
273 	 */
274 	private String onError = ON_ERROR_ABORT;
275 
276 	// //////////////////////////// Parser Configuration ////////////////////
277 
278 	/**
279 	 * Set the delimiter that separates SQL statements.
280 	 * 
281 	 * @since 1.0
282 	 * @parameter expression="${delimiter}" default-value=";"
283 	 */
284 	private String delimiter = ";";
285 
286 	/**
287 	 * <p>
288 	 * The delimiter type takes two values - "normal" and "row". Normal means
289 	 * that any occurrence of the delimiter terminate the SQL command whereas
290 	 * with row, only a line containing just the delimiter is recognized as the
291 	 * end of the command.
292 	 * </p>
293 	 * <p>
294 	 * For example, set this to "go" and delimiterType to "row" for Sybase ASE
295 	 * or MS SQL Server.
296 	 * </p>
297 	 * 
298 	 * @since 1.2
299 	 * @parameter expression="${delimiterType}" default-value="normal"
300 	 */
301 	private String delimiterType = DelimiterType.NORMAL;
302 
303 	/**
304 	 * Set the order in which the SQL files will be executed. Possible values
305 	 * are <code>ascending</code> and <code>descending</code>. Any other value
306 	 * means that no sorting will be performed.
307 	 * 
308 	 * @since 1.1
309 	 * @parameter expression="${orderFile}"
310 	 */
311 	private String orderFile = null;
312 
313 	/**
314 	 * When <code>true</code>, the whole SQL content in <code>sqlCommand</code>,
315 	 * <code>srcFiles</code> and <code>fileset</code> are sent directly to JDBC
316 	 * in one SQL statement. This option is for executing database stored
317 	 * procedures/functions.
318 	 * 
319 	 * @deprecated used <i>delimiterType<i> instead.
320 	 * @since 1.1
321 	 * @parameter expression="${enableBlockMode}"
322 	 */
323 
324 	private boolean enableBlockMode = false;
325 
326 	/**
327 	 * Keep the format of an SQL block.
328 	 * 
329 	 * @since 1.1
330 	 * @parameter expression="${keepFormat}" default-value="false"
331 	 */
332 	private boolean keepFormat = false;
333 
334 	// /////////////////////////////////////////////////////////////////////////////////////
335 	/**
336 	 * Print SQL results.
337 	 * 
338 	 * @parameter
339 	 * @since 1.3
340 	 */
341 	private boolean printResultSet = false;
342 
343 	/**
344 	 * Print header columns.
345 	 */
346 	private boolean showheaders = true;
347 
348 	/**
349 	 * Dump the SQL exection's output to a file. Default is stdout.
350 	 * 
351 	 * @parameter
352 	 * @since 1.3
353 	 */
354 	private File outputFile;
355 
356 	/**
357 	 * @parameter default-value=","
358 	 * @since 1.4
359 	 */
360 	private String outputDelimiter;
361 
362 	/**
363 	 * Encoding to use when reading SQL statements from a file.
364 	 * 
365 	 * @parameter expression="${encoding}" default-value=
366 	 *            "${project.build.sourceEncoding}"
367 	 * @since 1.1
368 	 */
369 	private String encoding = "";
370 
371 	/**
372 	 * Append to an existing file or overwrite it?
373 	 */
374 	private boolean append = false;
375 
376 	/**
377 	 * Argument to Statement.setEscapeProcessing If you want the driver to use
378 	 * regular SQL syntax then set this to false.
379 	 * 
380 	 * @since 1.4
381 	 * @parameter expression="${escapeProcessing}" default-value="true"
382 	 */
383 	private boolean escapeProcessing = true;
384 
385 	// //////////////////////////////// Internal
386 	// properties//////////////////////
387 
388 	/**
389 	 * number of successful executed statements
390 	 */
391 	private int successfulStatements = 0;
392 
393 	/**
394 	 * number of total executed statements
395 	 */
396 	private int totalStatements = 0;
397 
398 	/**
399 	 * Database connection
400 	 */
401 	private Connection conn = null;
402 
403 	/**
404 	 * SQL statement
405 	 */
406 	private Statement statement = null;
407 
408 	/**
409 	 * SQL transactions to perform
410 	 */
411 	private Vector<Transaction> transactions = new Vector<Transaction>();
412 
413 	/**
414 	 * @component role="org.apache.maven.shared.filtering.MavenFileFilter"
415 	 * @since 1.4
416 	 */
417 	private MavenFileFilter fileFilter;
418 
419 	/**
420 	 * Set to true if you want to filter the srcFiles using system-, user- and
421 	 * project properties
422 	 * 
423 	 * @parameter
424 	 * @since 1.4
425 	 */
426 	private boolean enableFiltering;
427 
428 	/**
429 	 * Interpolator especially for braceless expressions
430 	 */
431 	// private Interpolator interpolator = new
432 	// RegexBasedInterpolator("\\$([^\\s;)]+?)", "(?=[\\s;)])");
433 
434 	/**
435 	 * Add a SQL transaction to execute
436 	 * 
437 	 * @return a new SqlExecMojo.Transaction
438 	 */
439 	public Transaction createTransaction() {
440 		Transaction t = new Transaction();
441 		transactions.addElement(t);
442 		return t;
443 	}
444 
445 	/**
446 	 * Set an inline SQL command to execute. NB: Properties are not expanded in
447 	 * this text.
448 	 * 
449 	 * @param sql
450 	 *            the sql statement to add
451 	 */
452 	public void addText(String sql) {
453 		this.sqlCommand += sql;
454 	}
455 
456 	/**
457 	 * Set the file encoding to use on the SQL files read in
458 	 * 
459 	 * @param encoding
460 	 *            the encoding to use on the files
461 	 */
462 	public void setEncoding(String encoding) {
463 		this.encoding = encoding;
464 	}
465 
466 	/**
467 	 * Set the delimiter that separates SQL statements. Defaults to
468 	 * &quot;;&quot;;
469 	 * 
470 	 * @param delimiter
471 	 *            the new delimiter
472 	 */
473 	public void setDelimiter(String delimiter) {
474 		this.delimiter = delimiter;
475 	}
476 
477 	/**
478 	 * Set the delimiter type: "normal" or "row" (default "normal").
479 	 * 
480 	 * @param delimiterType
481 	 *            the new delimiterType
482 	 */
483 	public void setDelimiterType(String delimiterType) {
484 		this.delimiterType = delimiterType;
485 	}
486 
487 	/**
488 	 * Print result sets from the statements; optional, default false
489 	 * 
490 	 * @param print
491 	 *            <code>true</code> to print the resultset, otherwise
492 	 *            <code>false</code>
493 	 * @deprecated typo, use setPrintResultSet()
494 	 */
495 	public void setPrintResutlSet(boolean print) {
496 		setPrintResultSet(print);
497 	}
498 
499 	/**
500 	 * Print result sets from the statements; optional, default false
501 	 * 
502 	 * @param print
503 	 *            <code>true</code> to print the resultset, otherwise
504 	 *            <code>false</code>
505 	 */
506 	public void setPrintResultSet(boolean print) {
507 		this.printResultSet = print;
508 	}
509 
510 	/**
511 	 * Print headers for result sets from the statements; optional, default
512 	 * true.
513 	 * 
514 	 * @param showheaders
515 	 *            <code>true</code> to show the headers, otherwise
516 	 *            <code>false</code>
517 	 */
518 	public void setShowheaders(boolean showheaders) {
519 		this.showheaders = showheaders;
520 	}
521 
522 	/**
523 	 * Set the output file;
524 	 * 
525 	 * @param output
526 	 *            the output file
527 	 */
528 	public void setOutputFile(File output) {
529 		this.outputFile = output;
530 	}
531 
532 	/**
533 	 * whether output should be appended to or overwrite an existing file.
534 	 * Defaults to false.
535 	 * 
536 	 * @param append
537 	 *            <code>true</code> to append, otherwise <code>false</code> to
538 	 *            overwrite
539 	 */
540 	public void setAppend(boolean append) {
541 		this.append = append;
542 	}
543 
544 	/**
545 	 * whether or not format should be preserved. Defaults to false.
546 	 * 
547 	 * @param keepformat
548 	 *            The keepformat to set
549 	 */
550 	public void setKeepFormat(boolean keepformat) {
551 		this.keepFormat = keepformat;
552 	}
553 
554 	/**
555 	 * Set escape processing for statements.
556 	 * 
557 	 * @param enable
558 	 *            <code>true</code> to escape, otherwiser <code>false</code>
559 	 */
560 	public void setEscapeProcessing(boolean enable) {
561 		escapeProcessing = enable;
562 	}
563 
564 	/**
565 	 * <p>
566 	 * Determine if the mojo execution should get skipped.
567 	 * </p>
568 	 * This is the case if:
569 	 * <ul>
570 	 * <li>{@link #skip} is <code>true</code></li>
571 	 * <li>if the mojo gets executed on a project with packaging type 'pom' and
572 	 * {@link #forceMojoExecution} is <code>false</code></li>
573 	 * </ul>
574 	 * 
575 	 * @return <code>true</code> if the mojo execution should be skipped.
576 	 */
577 	protected boolean skipMojo() {
578 		if (skip) {
579 			getLog().info("Skip sql execution");
580 			return true;
581 		}
582 
583 		if (!forceMojoExecution && project != null && "pom".equals(project.getPackaging())) {
584 			getLog().info("Skipping sql execution for project with packaging type 'pom'");
585 			return true;
586 		}
587 
588 		return false;
589 	}
590 
591 	/**
592 	 * Load the sql file and then execute it
593 	 * 
594 	 * @throws MojoExecutionException
595 	 */
596 	public void execute() throws MojoExecutionException {
597 
598 		if (skipMojo()) {
599 			return;
600 		}
601 
602 		successfulStatements = 0;
603 
604 		totalStatements = 0;
605 
606 		loadUserInfoFromSettings();
607 
608 		addCommandToTransactions();
609 
610 		addFilesToTransactions();
611 
612 		addFileSetToTransactions();
613 
614 		addResourcesToTransactions();
615 
616 		sortTransactions();
617 
618 		try {
619 			conn = getConnection();
620 		} catch (SQLException e) {
621 			if (!this.skipOnConnectionError) {
622 				throw new MojoExecutionException(e.getMessage(), e);
623 			} else {
624 				// error on get connection and user asked to skip the rest
625 				return;
626 			}
627 		}
628 
629 		try {
630 			statement = conn.createStatement();
631 			statement.setEscapeProcessing(escapeProcessing);
632 
633 			PrintStream out = System.out;
634 			try {
635 				if (outputFile != null) {
636 					getLog().debug("Opening PrintStream to output file " + outputFile);
637 					out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile.getAbsolutePath(),
638 							append)));
639 				}
640 
641 				// Process all transactions
642 				for (Enumeration<Transaction> e = transactions.elements(); e.hasMoreElements();) {
643 					Transaction t = e.nextElement();
644 
645 					t.runTransaction(out);
646 
647 					if (!autocommit) {
648 						getLog().debug("Committing transaction");
649 						conn.commit();
650 					}
651 				}
652 			} finally {
653 				if (out != null && out != System.out) {
654 					out.close();
655 				}
656 			}
657 		} catch (IOException e) {
658 			throw new MojoExecutionException(e.getMessage(), e);
659 		} catch (SQLException e) {
660 			if (!autocommit && conn != null && ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
661 				try {
662 					conn.rollback();
663 				} catch (SQLException ex) {
664 					// ignore
665 				}
666 			}
667 			throw new MojoExecutionException(e.getMessage(), e);
668 		} finally {
669 			try {
670 				if (statement != null) {
671 					statement.close();
672 				}
673 				if (conn != null) {
674 					conn.close();
675 				}
676 			} catch (SQLException ex) {
677 				// ignore
678 			}
679 		}
680 
681 		getLog().info(
682 				getSuccessfulStatements() + " of " + getTotalStatements() + " SQL statements executed successfully");
683 
684 		if (ON_ERROR_ABORT_AFTER.equalsIgnoreCase(getOnError()) && totalStatements != successfulStatements) {
685 			throw new MojoExecutionException("Some SQL statements failed to execute");
686 		}
687 
688 	}
689 
690 	/**
691 	 * Add sql command to transactions list.
692 	 * 
693 	 */
694 	private void addCommandToTransactions() {
695 		createTransaction().addText(sqlCommand.trim());
696 	}
697 
698 	/**
699 	 * Add user sql fileset to transation list
700 	 * 
701 	 */
702 	private void addFileSetToTransactions() {
703 		String[] includedFiles;
704 		if (fileset != null) {
705 			fileset.scan();
706 			includedFiles = fileset.getIncludedFiles();
707 		} else {
708 			includedFiles = new String[0];
709 		}
710 
711 		for (int j = 0; j < includedFiles.length; j++) {
712 			createTransaction().setSrc(new File(fileset.getBasedir(), includedFiles[j]));
713 		}
714 	}
715 
716 	protected Resource[] getResources(String[] locations) throws MojoExecutionException {
717 		ResourceLoader loader = new DefaultResourceLoader();
718 		Resource[] resources = new Resource[locations.length];
719 		for (int i = 0; i < locations.length; i++) {
720 			String location = locations[i];
721 			Resource resource = loader.getResource(location);
722 			if (!resource.exists()) {
723 				throw new MojoExecutionException("Resource " + location + " was not found");
724 			}
725 			resources[i] = resource;
726 		}
727 		return resources;
728 	}
729 
730 	protected void copy(Resource resource, File file) throws IOException {
731 		InputStream in = resource.getInputStream();
732 		OutputStream out = new FileOutputStream(file);
733 		IOUtils.copyLarge(in, out);
734 	}
735 
736 	/**
737 	 * Add user input of srcFiles to transaction list.
738 	 * 
739 	 * @throws MojoExecutionException
740 	 */
741 	private void addResourcesToTransactions() throws MojoExecutionException {
742 		String[] locations = getResourceLocations();
743 		Resource[] resources = getResources(locations);
744 
745 		MavenFileFilterRequest request = new MavenFileFilterRequest();
746 		request.setEncoding(encoding);
747 		request.setMavenSession(mavenSession);
748 		request.setMavenProject(project);
749 		request.setFiltering(enableFiltering);
750 		for (int i = 0; i < resources.length; i++) {
751 			Resource resource = resources[i];
752 			String filename = resource.getFilename();
753 			String basename = FileUtils.basename(filename);
754 			String extension = FileUtils.extension(filename);
755 			File sourceFile = FileUtils.createTempFile(basename, extension, null);
756 
757 			try {
758 				copy(resource, sourceFile);
759 			} catch (IOException e) {
760 				throw new MojoExecutionException("Error copying resource " + resource + "to a temp file", e);
761 			}
762 
763 			File targetFile = FileUtils.createTempFile(basename, extension, null);
764 			if (!getLog().isDebugEnabled()) {
765 				targetFile.deleteOnExit();
766 				sourceFile.deleteOnExit();
767 			}
768 
769 			request.setFrom(sourceFile);
770 			request.setTo(targetFile);
771 
772 			try {
773 				fileFilter.copyFile(request);
774 			} catch (MavenFilteringException e) {
775 				throw new MojoExecutionException(e.getMessage());
776 			}
777 
778 			createTransaction().setSrc(targetFile);
779 		}
780 	}
781 
782 	/**
783 	 * Add user input of srcFiles to transaction list.
784 	 * 
785 	 * @throws MojoExecutionException
786 	 */
787 	private void addFilesToTransactions() throws MojoExecutionException {
788 		File[] files = getSrcFiles();
789 
790 		MavenFileFilterRequest request = new MavenFileFilterRequest();
791 		request.setEncoding(encoding);
792 		request.setMavenSession(mavenSession);
793 		request.setMavenProject(project);
794 		request.setFiltering(enableFiltering);
795 		for (int i = 0; files != null && i < files.length; ++i) {
796 			if (files[i] != null && !files[i].exists()) {
797 				throw new MojoExecutionException(files[i].getPath() + " not found.");
798 			}
799 
800 			File sourceFile = files[i];
801 			String basename = FileUtils.basename(sourceFile.getName());
802 			String extension = FileUtils.extension(sourceFile.getName());
803 			File targetFile = FileUtils.createTempFile(basename, extension, null);
804 			if (!getLog().isDebugEnabled()) {
805 				targetFile.deleteOnExit();
806 			}
807 
808 			request.setFrom(sourceFile);
809 			request.setTo(targetFile);
810 
811 			try {
812 				fileFilter.copyFile(request);
813 			} catch (MavenFilteringException e) {
814 				throw new MojoExecutionException(e.getMessage());
815 			}
816 
817 			createTransaction().setSrc(targetFile);
818 		}
819 	}
820 
821 	/**
822 	 * Sort the transaction list.
823 	 */
824 	private void sortTransactions() {
825 		if (FILE_SORTING_ASC.equalsIgnoreCase(this.orderFile)) {
826 			Collections.sort(transactions);
827 		} else if (FILE_SORTING_DSC.equalsIgnoreCase(this.orderFile)) {
828 			Collections.sort(transactions, Collections.reverseOrder());
829 		}
830 	}
831 
832 	/**
833 	 * Load username password from settings if user has not set them in JVM
834 	 * properties
835 	 * 
836 	 * @throws MojoExecutionException
837 	 */
838 	private void loadUserInfoFromSettings() throws MojoExecutionException {
839 		if (this.settingsKey == null) {
840 			this.settingsKey = getUrl();
841 		}
842 
843 		if ((getUsername() == null || getPassword() == null) && (settings != null)) {
844 			Server server = this.settings.getServer(this.settingsKey);
845 
846 			if (server != null) {
847 				if (getUsername() == null) {
848 					setUsername(server.getUsername());
849 				}
850 
851 				if (getPassword() == null) {
852 					setPassword(server.getPassword());
853 				}
854 			}
855 		}
856 
857 		if (getUsername() == null) {
858 			// allow emtpy username
859 			setUsername("");
860 		}
861 
862 		if (getPassword() == null) {
863 			// allow emtpy password
864 			setPassword("");
865 		}
866 	}
867 
868 	/**
869 	 * Creates a new Connection as using the driver, url, userid and password
870 	 * specified.
871 	 * 
872 	 * The calling method is responsible for closing the connection.
873 	 * 
874 	 * @return Connection the newly created connection.
875 	 * @throws MojoExecutionException
876 	 *             if the UserId/Password/Url is not set or there is no suitable
877 	 *             driver or the driver fails to load.
878 	 * @throws SQLException
879 	 *             if there is problem getting connection with valid url
880 	 * 
881 	 */
882 	private Connection getConnection() throws MojoExecutionException, SQLException {
883 		getLog().debug("connecting to " + getUrl());
884 		Properties info = new Properties();
885 		info.put("user", getUsername());
886 
887 		if (!enableAnonymousPassword) {
888 			info.put("password", getPassword());
889 		}
890 
891 		info.putAll(this.getDriverProperties());
892 
893 		Driver driverInstance = null;
894 
895 		try {
896 			Class<?> dc = Class.forName(getDriver());
897 			driverInstance = (Driver) dc.newInstance();
898 		} catch (ClassNotFoundException e) {
899 			throw new MojoExecutionException("Driver class not found: " + getDriver(), e);
900 		} catch (Exception e) {
901 			throw new MojoExecutionException("Failure loading driver: " + getDriver(), e);
902 		}
903 
904 		Connection conn = driverInstance.connect(getUrl(), info);
905 
906 		if (conn == null) {
907 			// Driver doesn't understand the URL
908 			throw new SQLException("No suitable Driver for " + getUrl());
909 		}
910 
911 		conn.setAutoCommit(autocommit);
912 		return conn;
913 	}
914 
915 	/**
916 	 * parse driverProperties into Properties set
917 	 * 
918 	 * @return the driver properties
919 	 * @throws MojoExecutionException
920 	 */
921 	protected Properties getDriverProperties() throws MojoExecutionException {
922 		Properties properties = new Properties();
923 
924 		if (!StringUtils.isEmpty(this.driverProperties)) {
925 			String[] tokens = StringUtils.split(this.driverProperties, ",");
926 			for (int i = 0; i < tokens.length; ++i) {
927 				String[] keyValueTokens = StringUtils.split(tokens[i].trim(), "=");
928 				if (keyValueTokens.length != 2) {
929 					throw new MojoExecutionException("Invalid JDBC Driver properties: " + this.driverProperties);
930 				}
931 
932 				properties.setProperty(keyValueTokens[0], keyValueTokens[1]);
933 
934 			}
935 		}
936 
937 		return properties;
938 	}
939 
940 	/**
941 	 * read in lines and execute them
942 	 * 
943 	 * @param reader
944 	 *            the reader
945 	 * @param out
946 	 *            the outputstream
947 	 * @throws SQLException
948 	 * @throws IOException
949 	 */
950 	private void runStatements(Reader reader, PrintStream out) throws SQLException, IOException {
951 		String line;
952 
953 		if (enableBlockMode) {
954 			// no need to parse the content, ship it directly to jdbc in one sql
955 			// statement
956 			line = IOUtil.toString(reader);
957 			execSQL(line, out);
958 			return;
959 		}
960 
961 		StringBuffer sql = new StringBuffer();
962 
963 		BufferedReader in = new BufferedReader(reader);
964 
965 		while ((line = in.readLine()) != null) {
966 			if (!keepFormat) {
967 				line = line.trim();
968 			}
969 
970 			if (!keepFormat) {
971 				if (line.startsWith("//")) {
972 					continue;
973 				}
974 				if (line.startsWith("--")) {
975 					continue;
976 				}
977 				StringTokenizer st = new StringTokenizer(line);
978 				if (st.hasMoreTokens()) {
979 					String token = st.nextToken();
980 					if ("REM".equalsIgnoreCase(token)) {
981 						continue;
982 					}
983 				}
984 			}
985 
986 			if (!keepFormat) {
987 				sql.append(" ").append(line);
988 			} else {
989 				sql.append("\n").append(line);
990 			}
991 
992 			// SQL defines "--" as a comment to EOL
993 			// and in Oracle it may contain a hint
994 			// so we cannot just remove it, instead we must end it
995 			if (!keepFormat) {
996 				if (SqlSplitter.containsSqlEnd(line, delimiter) == SqlSplitter.NO_END) {
997 					sql.append("\n");
998 				}
999 			}
1000 
1001 			if ((delimiterType.equals(DelimiterType.NORMAL) && SqlSplitter.containsSqlEnd(line, delimiter) > 0)
1002 					|| (delimiterType.equals(DelimiterType.ROW) && line.trim().equals(delimiter))) {
1003 				execSQL(sql.substring(0, sql.length() - delimiter.length()), out);
1004 				sql.setLength(0); // clean buffer
1005 			}
1006 		}
1007 
1008 		// Catch any statements not followed by ;
1009 		if (!sql.toString().equals("")) {
1010 			execSQL(sql.toString(), out);
1011 		}
1012 	}
1013 
1014 	/**
1015 	 * Exec the sql statement.
1016 	 * 
1017 	 * @param sql
1018 	 *            query to execute
1019 	 * @param out
1020 	 *            the outputstream
1021 	 */
1022 	private void execSQL(String sql, PrintStream out) throws SQLException {
1023 		// Check and ignore empty statements
1024 		if ("".equals(sql.trim())) {
1025 			return;
1026 		}
1027 
1028 		ResultSet resultSet = null;
1029 		try {
1030 			totalStatements++;
1031 			getLog().debug("SQL: " + sql);
1032 
1033 			boolean ret;
1034 			int updateCountTotal = 0;
1035 
1036 			ret = statement.execute(sql);
1037 			do {
1038 				if (!ret) {
1039 					int updateCount = statement.getUpdateCount();
1040 					if (updateCount != -1) {
1041 						updateCountTotal += updateCount;
1042 					}
1043 				} else {
1044 					resultSet = statement.getResultSet();
1045 					if (printResultSet) {
1046 						printResultSet(resultSet, out);
1047 					}
1048 				}
1049 				ret = statement.getMoreResults();
1050 			} while (ret);
1051 
1052 			getLog().debug(updateCountTotal + " rows affected");
1053 
1054 			if (printResultSet) {
1055 				StringBuffer line = new StringBuffer();
1056 				line.append(updateCountTotal).append(" rows affected");
1057 				out.println(line);
1058 			}
1059 
1060 			SQLWarning warning = conn.getWarnings();
1061 			while (warning != null) {
1062 				getLog().debug(warning + " sql warning");
1063 				warning = warning.getNextWarning();
1064 			}
1065 			conn.clearWarnings();
1066 			successfulStatements++;
1067 		} catch (SQLException e) {
1068 			getLog().error("Failed to execute: " + sql);
1069 			if (ON_ERROR_ABORT.equalsIgnoreCase(getOnError())) {
1070 				throw e;
1071 			}
1072 			getLog().error(e.toString());
1073 		} finally {
1074 			if (resultSet != null) {
1075 				resultSet.close();
1076 			}
1077 		}
1078 	}
1079 
1080 	/**
1081 	 * print any results in the result set.
1082 	 * 
1083 	 * @param rs
1084 	 *            the resultset to print information about
1085 	 * @param out
1086 	 *            the place to print results
1087 	 * @throws SQLException
1088 	 *             on SQL problems.
1089 	 */
1090 	private void printResultSet(ResultSet rs, PrintStream out) throws SQLException {
1091 		if (rs != null) {
1092 			getLog().debug("Processing new result set.");
1093 			ResultSetMetaData md = rs.getMetaData();
1094 			int columnCount = md.getColumnCount();
1095 			StringBuffer line = new StringBuffer();
1096 			if (showheaders) {
1097 				boolean first = true;
1098 				for (int col = 1; col <= columnCount; col++) {
1099 					String columnValue = md.getColumnName(col);
1100 
1101 					if (columnValue != null) {
1102 						columnValue = columnValue.trim();
1103 
1104 						if (",".equals(outputDelimiter)) {
1105 							columnValue = StringEscapeUtils.escapeCsv(columnValue);
1106 						}
1107 					}
1108 
1109 					if (first) {
1110 						first = false;
1111 					} else {
1112 						line.append(outputDelimiter);
1113 					}
1114 					line.append(columnValue);
1115 				}
1116 				out.println(line);
1117 				line = new StringBuffer();
1118 			}
1119 			while (rs.next()) {
1120 				boolean first = true;
1121 				for (int col = 1; col <= columnCount; col++) {
1122 					String columnValue = rs.getString(col);
1123 					if (columnValue != null) {
1124 						columnValue = columnValue.trim();
1125 
1126 						if (",".equals(outputDelimiter)) {
1127 							columnValue = StringEscapeUtils.escapeCsv(columnValue);
1128 						}
1129 					}
1130 
1131 					if (first) {
1132 						first = false;
1133 					} else {
1134 						line.append(outputDelimiter);
1135 					}
1136 					line.append(columnValue);
1137 				}
1138 				out.println(line);
1139 				line = new StringBuffer();
1140 			}
1141 		}
1142 		out.println();
1143 	}
1144 
1145 	/**
1146 	 * Contains the definition of a new transaction element. Transactions allow
1147 	 * several files or blocks of statements to be executed using the same JDBC
1148 	 * connection and commit operation in between.
1149 	 */
1150 	private class Transaction implements Comparable<Transaction> {
1151 		private File tSrcFile = null;
1152 
1153 		private String tSqlCommand = "";
1154 
1155 		/**
1156          *
1157          */
1158 		public void setSrc(File src) {
1159 			this.tSrcFile = src;
1160 		}
1161 
1162 		/**
1163          *
1164          */
1165 		public void addText(String sql) {
1166 			this.tSqlCommand += sql;
1167 		}
1168 
1169 		/**
1170          *
1171          */
1172 		private void runTransaction(PrintStream out) throws IOException, SQLException {
1173 			if (tSqlCommand.length() != 0) {
1174 				getLog().info("Executing commands");
1175 
1176 				runStatements(new StringReader(tSqlCommand), out);
1177 			}
1178 
1179 			if (tSrcFile != null) {
1180 				getLog().info("Executing file: " + tSrcFile.getAbsolutePath());
1181 
1182 				Reader reader = null;
1183 
1184 				if (StringUtils.isEmpty(encoding)) {
1185 					reader = new FileReader(tSrcFile);
1186 				} else {
1187 					reader = new InputStreamReader(new FileInputStream(tSrcFile), encoding);
1188 				}
1189 
1190 				try {
1191 					runStatements(reader, out);
1192 				} finally {
1193 					reader.close();
1194 				}
1195 			}
1196 		}
1197 
1198 		public int compareTo(Transaction transaction) {
1199 
1200 			if (transaction.tSrcFile == null) {
1201 				if (this.tSrcFile == null) {
1202 					return 0;
1203 				} else {
1204 					return Integer.MAX_VALUE;
1205 				}
1206 			} else {
1207 				if (this.tSrcFile == null) {
1208 					return Integer.MIN_VALUE;
1209 				} else {
1210 					return this.tSrcFile.compareTo(transaction.tSrcFile);
1211 				}
1212 			}
1213 		}
1214 	}
1215 
1216 	//
1217 	// helper accessors for unit test purposes
1218 	//
1219 
1220 	public String getUsername() {
1221 		return this.username;
1222 	}
1223 
1224 	public void setUsername(String username) {
1225 		this.username = username;
1226 	}
1227 
1228 	public String getPassword() {
1229 		return this.password;
1230 	}
1231 
1232 	public void setPassword(String password) {
1233 		this.password = password;
1234 	}
1235 
1236 	public String getUrl() {
1237 		return this.url;
1238 	}
1239 
1240 	public void setUrl(String url) {
1241 		this.url = url;
1242 	}
1243 
1244 	public String getDriver() {
1245 		return this.driver;
1246 	}
1247 
1248 	public void setDriver(String driver) {
1249 		this.driver = driver;
1250 	}
1251 
1252 	void setAutocommit(boolean autocommit) {
1253 		this.autocommit = autocommit;
1254 	}
1255 
1256 	void setFileset(Fileset fileset) {
1257 		this.fileset = fileset;
1258 	}
1259 
1260 	public File[] getSrcFiles() {
1261 		return this.srcFiles;
1262 	}
1263 
1264 	public void setSrcFiles(File[] files) {
1265 		this.srcFiles = files;
1266 	}
1267 
1268 	public String getOrderFile() {
1269 		return this.orderFile;
1270 	}
1271 
1272 	public void setOrderFile(String orderFile) {
1273 		if (FILE_SORTING_ASC.equalsIgnoreCase(orderFile)) {
1274 			this.orderFile = FILE_SORTING_ASC;
1275 		} else if (FILE_SORTING_DSC.equalsIgnoreCase(orderFile)) {
1276 			this.orderFile = FILE_SORTING_DSC;
1277 		} else {
1278 			throw new IllegalArgumentException(orderFile + " is not a valid value for orderFile, only '"
1279 					+ FILE_SORTING_ASC + "' or '" + FILE_SORTING_DSC + "'.");
1280 		}
1281 	}
1282 
1283 	/**
1284 	 * @deprecated use {@link #getSuccessfulStatements()}
1285 	 */
1286 	int getGoodSqls() {
1287 		return this.getSuccessfulStatements();
1288 	}
1289 
1290 	/**
1291 	 * Number of SQL statements executed so far that caused errors.
1292 	 * 
1293 	 * @return the number
1294 	 */
1295 	public int getSuccessfulStatements() {
1296 		return successfulStatements;
1297 	}
1298 
1299 	/**
1300 	 * Number of SQL statements executed so far, including the ones that caused
1301 	 * errors.
1302 	 * 
1303 	 * @return the number
1304 	 */
1305 	public int getTotalStatements() {
1306 		return totalStatements;
1307 	}
1308 
1309 	public String getOnError() {
1310 		return this.onError;
1311 	}
1312 
1313 	public void setOnError(String action) {
1314 		if (ON_ERROR_ABORT.equalsIgnoreCase(action)) {
1315 			this.onError = ON_ERROR_ABORT;
1316 		} else if (ON_ERROR_CONTINUE.equalsIgnoreCase(action)) {
1317 			this.onError = ON_ERROR_CONTINUE;
1318 		} else if (ON_ERROR_ABORT_AFTER.equalsIgnoreCase(action)) {
1319 			this.onError = ON_ERROR_ABORT_AFTER;
1320 		} else {
1321 			throw new IllegalArgumentException(action + " is not a valid value for onError, only '" + ON_ERROR_ABORT
1322 					+ "', '" + ON_ERROR_ABORT_AFTER + "', or '" + ON_ERROR_CONTINUE + "'.");
1323 		}
1324 	}
1325 
1326 	void setSettings(Settings settings) {
1327 		this.settings = settings;
1328 	}
1329 
1330 	void setSettingsKey(String key) {
1331 		this.settingsKey = key;
1332 	}
1333 
1334 	void setSkip(boolean skip) {
1335 		this.skip = skip;
1336 	}
1337 
1338 	public void setDriverProperties(String driverProperties) {
1339 		this.driverProperties = driverProperties;
1340 	}
1341 
1342 	public boolean isEnableBlockMode() {
1343 		return enableBlockMode;
1344 	}
1345 
1346 	public void setEnableBlockMode(boolean enableBlockMode) {
1347 		this.enableBlockMode = enableBlockMode;
1348 	}
1349 
1350 	public String getSqlCommand() {
1351 		return sqlCommand;
1352 	}
1353 
1354 	public void setSqlCommand(String sqlCommand) {
1355 		this.sqlCommand = sqlCommand;
1356 	}
1357 
1358 	public Vector<Transaction> getTransactions() {
1359 		return transactions;
1360 	}
1361 
1362 	public void setTransactions(Vector<Transaction> transactions) {
1363 		this.transactions = transactions;
1364 	}
1365 
1366 	public void setFileFilter(MavenFileFilter filter) {
1367 		this.fileFilter = filter;
1368 	}
1369 
1370 	public String[] getResourceLocations() {
1371 		return resourceLocations;
1372 	}
1373 
1374 	public void setResourceLocations(String[] resourceLocations) {
1375 		this.resourceLocations = resourceLocations;
1376 	}
1377 }