You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

JDBCTask.java 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.sql.Connection;
  56. import java.sql.DatabaseMetaData;
  57. import java.sql.Driver;
  58. import java.sql.SQLException;
  59. import java.util.Hashtable;
  60. import java.util.Properties;
  61. import org.apache.tools.ant.AntClassLoader;
  62. import org.apache.tools.ant.BuildException;
  63. import org.apache.tools.ant.Project;
  64. import org.apache.tools.ant.Task;
  65. import org.apache.tools.ant.types.Path;
  66. import org.apache.tools.ant.types.Reference;
  67. /**
  68. * Handles JDBC configuration needed by SQL type tasks.
  69. * <p>
  70. * The following example class prints the contents of the first column of each row in TableName.
  71. *</p>
  72. *<code><pre>
  73. package examples;
  74. import java.sql.Connection;
  75. import java.sql.ResultSet;
  76. import java.sql.SQLException;
  77. import java.sql.Statement;
  78. import org.apache.tools.ant.BuildException;
  79. import org.apache.tools.ant.taskdefs.JDBCTask;
  80. public class SQLExampleTask extends JDBCTask {
  81. private String tableName;
  82. public void execute() throws BuildException {
  83. Connection conn = getConnection();
  84. Statement stmt=null;
  85. try {
  86. if (tableName == null ) {
  87. throw new BuildException("TableName must be specified",location);
  88. }
  89. String sql = "SELECT * FROM "+tableName;
  90. stmt= conn.createStatement();
  91. ResultSet rs = stmt.executeQuery(sql);
  92. while (rs.next()) {
  93. log(rs.getObject(1).toString());
  94. }
  95. } catch (SQLException e) {
  96. } finally {
  97. if (stmt != null) {
  98. try {stmt.close();}catch (SQLException ingore){}
  99. }
  100. if (conn != null) {
  101. try {conn.close();}catch (SQLException ingore){}
  102. }
  103. }
  104. }
  105. public void setTableName(String tableName) {
  106. this.tableName = tableName;
  107. }
  108. }
  109. </pre></code>
  110. * @author <a href="mailto:nick@chalko.com">Nick Chalko</a>
  111. * @author <a href="mailto:jeff@custommonkey.org">Jeff Martin</a>
  112. * @author <A href="mailto:gholam@xtra.co.nz">Michael McCallum</A>
  113. * @author <A href="mailto:tim.stephenson@sybase.com">Tim Stephenson</A>
  114. *
  115. * @since Ant 1.5
  116. *
  117. */
  118. public abstract class JDBCTask extends Task {
  119. /**
  120. * Used for caching loaders / driver. This is to avoid
  121. * getting an OutOfMemoryError when calling this task
  122. * multiple times in a row.
  123. */
  124. private static Hashtable loaderMap = new Hashtable(3);
  125. private boolean caching = true;
  126. private Path classpath;
  127. private AntClassLoader loader;
  128. /**
  129. * Autocommit flag. Default value is false
  130. */
  131. private boolean autocommit = false;
  132. /**
  133. * DB driver.
  134. */
  135. private String driver = null;
  136. /**
  137. * DB url.
  138. */
  139. private String url = null;
  140. /**
  141. * User name.
  142. */
  143. private String userId = null;
  144. /**
  145. * Password
  146. */
  147. private String password = null;
  148. /**
  149. * RDBMS Product needed for this SQL.
  150. **/
  151. private String rdbms = null;
  152. /**
  153. * RDBMS Version needed for this SQL.
  154. **/
  155. private String version = null;
  156. /**
  157. * Sets the classpath for loading the driver.
  158. * @param classpath The classpath to set
  159. */
  160. public void setClasspath(Path classpath) {
  161. this.classpath = classpath;
  162. }
  163. /**
  164. * Caching loaders / driver. This is to avoid
  165. * getting an OutOfMemoryError when calling this task
  166. * multiple times in a row; default: true
  167. * @param enable
  168. */
  169. public void setCaching(boolean enable) {
  170. caching = enable;
  171. }
  172. /**
  173. * Add a path to the classpath for loading the driver.
  174. */
  175. public Path createClasspath() {
  176. if (this.classpath == null) {
  177. this.classpath = new Path(getProject());
  178. }
  179. return this.classpath.createPath();
  180. }
  181. /**
  182. * Set the classpath for loading the driver
  183. * using the classpath reference.
  184. */
  185. public void setClasspathRef(Reference r) {
  186. createClasspath().setRefid(r);
  187. }
  188. /**
  189. * Class name of the JDBC driver; required.
  190. * @param driver The driver to set
  191. */
  192. public void setDriver(String driver) {
  193. this.driver = driver;
  194. }
  195. /**
  196. * Sets the database connection URL; required.
  197. * @param url The url to set
  198. */
  199. public void setUrl(String url) {
  200. this.url = url;
  201. }
  202. /**
  203. * Sets the password; required.
  204. * @param password The password to set
  205. */
  206. public void setPassword(String password) {
  207. this.password = password;
  208. }
  209. /**
  210. * Auto commit flag for database connection;
  211. * optional, default false.
  212. * @param autocommit The autocommit to set
  213. */
  214. public void setAutocommit(boolean autocommit) {
  215. this.autocommit = autocommit;
  216. }
  217. /**
  218. * Execute task only if the lower case product name
  219. * of the DB matches this
  220. * @param rdbms The rdbms to set
  221. */
  222. public void setRdbms(String rdbms) {
  223. this.rdbms = rdbms;
  224. }
  225. /**
  226. * Sets the version string, execute task only if
  227. * rdbms version match; optional.
  228. * @param version The version to set
  229. */
  230. public void setVersion(String version) {
  231. this.version = version;
  232. }
  233. /**
  234. * Verify we are connected to the correct RDBMS
  235. */
  236. protected boolean isValidRdbms(Connection conn) {
  237. if (rdbms == null && version == null) {
  238. return true;
  239. }
  240. try {
  241. DatabaseMetaData dmd = conn.getMetaData();
  242. if (rdbms != null) {
  243. String theVendor = dmd.getDatabaseProductName().toLowerCase();
  244. log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
  245. if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
  246. log("Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE);
  247. return false;
  248. }
  249. }
  250. if (version != null) {
  251. // XXX maybe better toLowerCase(Locale.US)
  252. String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
  253. log("Version = " + theVersion, Project.MSG_VERBOSE);
  254. if (theVersion == null
  255. || !(theVersion.startsWith(version) || theVersion.indexOf(" " + version) >= 0)) {
  256. log("Not the required version: \"" + version + "\"", Project.MSG_VERBOSE);
  257. return false;
  258. }
  259. }
  260. } catch (SQLException e) {
  261. // Could not get the required information
  262. log("Failed to obtain required RDBMS information", Project.MSG_ERR);
  263. return false;
  264. }
  265. return true;
  266. }
  267. protected static Hashtable getLoaderMap() {
  268. return loaderMap;
  269. }
  270. protected AntClassLoader getLoader() {
  271. return loader;
  272. }
  273. /**
  274. * Creates a new Connection as using the driver, url, userid and password specified.
  275. * The calling method is responsible for closing the connection.
  276. * @return Connection the newly created connection.
  277. * @throws BuildException if the UserId/Password/Url is not set or there is no suitable driver or the driver fails to load.
  278. */
  279. protected Connection getConnection() throws BuildException {
  280. if (userId == null) {
  281. throw new BuildException("User Id attribute must be set!", getLocation());
  282. }
  283. if (password == null) {
  284. throw new BuildException("Password attribute must be set!", getLocation());
  285. }
  286. if (url == null) {
  287. throw new BuildException("Url attribute must be set!", getLocation());
  288. }
  289. try {
  290. log("connecting to " + getUrl(), Project.MSG_VERBOSE);
  291. Properties info = new Properties();
  292. info.put("user", getUserId());
  293. info.put("password", getPassword());
  294. Connection conn = getDriver().connect(getUrl(), info);
  295. if (conn == null) {
  296. // Driver doesn't understand the URL
  297. throw new SQLException("No suitable Driver for " + url);
  298. }
  299. conn.setAutoCommit(autocommit);
  300. return conn;
  301. } catch (SQLException e) {
  302. throw new BuildException(e, getLocation());
  303. }
  304. }
  305. /**
  306. * Gets an instance of the required driver.
  307. * Uses the ant class loader and the optionally the provided classpath.
  308. * @return Driver
  309. * @throws BuildException
  310. */
  311. private Driver getDriver() throws BuildException {
  312. if (driver == null) {
  313. throw new BuildException("Driver attribute must be set!", getLocation());
  314. }
  315. Driver driverInstance = null;
  316. try {
  317. Class dc;
  318. if (classpath != null) {
  319. // check first that it is not already loaded otherwise
  320. // consecutive runs seems to end into an OutOfMemoryError
  321. // or it fails when there is a native library to load
  322. // several times.
  323. // this is far from being perfect but should work
  324. // in most cases.
  325. synchronized (loaderMap) {
  326. if (caching) {
  327. loader = (AntClassLoader) loaderMap.get(driver);
  328. }
  329. if (loader == null) {
  330. log(
  331. "Loading " + driver + " using AntClassLoader with classpath " + classpath,
  332. Project.MSG_VERBOSE);
  333. loader = getProject().createClassLoader(classpath);
  334. if (caching) {
  335. loaderMap.put(driver, loader);
  336. }
  337. } else {
  338. log(
  339. "Loading " + driver + " using a cached AntClassLoader.",
  340. Project.MSG_VERBOSE);
  341. }
  342. }
  343. dc = loader.loadClass(driver);
  344. } else {
  345. log("Loading " + driver + " using system loader.", Project.MSG_VERBOSE);
  346. dc = Class.forName(driver);
  347. }
  348. driverInstance = (Driver) dc.newInstance();
  349. } catch (ClassNotFoundException e) {
  350. throw new BuildException(
  351. "Class Not Found: JDBC driver " + driver + " could not be loaded",
  352. getLocation());
  353. } catch (IllegalAccessException e) {
  354. throw new BuildException(
  355. "Illegal Access: JDBC driver " + driver + " could not be loaded",
  356. getLocation());
  357. } catch (InstantiationException e) {
  358. throw new BuildException(
  359. "Instantiation Exception: JDBC driver " + driver + " could not be loaded",
  360. getLocation());
  361. }
  362. return driverInstance;
  363. }
  364. public void isCaching(boolean value) {
  365. caching = value;
  366. }
  367. /**
  368. * Gets the classpath.
  369. * @return Returns a Path
  370. */
  371. public Path getClasspath() {
  372. return classpath;
  373. }
  374. /**
  375. * Gets the autocommit.
  376. * @return Returns a boolean
  377. */
  378. public boolean isAutocommit() {
  379. return autocommit;
  380. }
  381. /**
  382. * Gets the url.
  383. * @return Returns a String
  384. */
  385. public String getUrl() {
  386. return url;
  387. }
  388. /**
  389. * Gets the userId.
  390. * @return Returns a String
  391. */
  392. public String getUserId() {
  393. return userId;
  394. }
  395. /**
  396. * Set the user name for the connection; required.
  397. * @param userId The userId to set
  398. */
  399. public void setUserid(String userId) {
  400. this.userId = userId;
  401. }
  402. /**
  403. * Gets the password.
  404. * @return Returns a String
  405. */
  406. public String getPassword() {
  407. return password;
  408. }
  409. /**
  410. * Gets the rdbms.
  411. * @return Returns a String
  412. */
  413. public String getRdbms() {
  414. return rdbms;
  415. }
  416. /**
  417. * Gets the version.
  418. * @return Returns a String
  419. */
  420. public String getVersion() {
  421. return version;
  422. }
  423. }