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