1. /*
  2. * Copyright 2002-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs;
  18. import java.sql.Connection;
  19. import java.sql.DatabaseMetaData;
  20. import java.sql.Driver;
  21. import java.sql.SQLException;
  22. import java.util.Hashtable;
  23. import java.util.Properties;
  24. import org.apache.tools.ant.AntClassLoader;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.Project;
  27. import org.apache.tools.ant.Task;
  28. import org.apache.tools.ant.types.Path;
  29. import org.apache.tools.ant.types.Reference;
  30. /**
  31. * Handles JDBC configuration needed by SQL type tasks.
  32. * <p>
  33. * The following example class prints the contents of the first column of each row in TableName.
  34. *</p>
  35. *<code><pre>
  36. package examples;
  37. import java.sql.Connection;
  38. import java.sql.ResultSet;
  39. import java.sql.SQLException;
  40. import java.sql.Statement;
  41. import org.apache.tools.ant.BuildException;
  42. import org.apache.tools.ant.taskdefs.JDBCTask;
  43. public class SQLExampleTask extends JDBCTask {
  44. private String tableName;
  45. public void execute() throws BuildException {
  46. Connection conn = getConnection();
  47. Statement stmt=null;
  48. try {
  49. if (tableName == null) {
  50. throw new BuildException("TableName must be specified",location);
  51. }
  52. String sql = "SELECT * FROM "+tableName;
  53. stmt= conn.createStatement();
  54. ResultSet rs = stmt.executeQuery(sql);
  55. while (rs.next()) {
  56. log(rs.getObject(1).toString());
  57. }
  58. } catch (SQLException e) {
  59. } finally {
  60. if (stmt != null) {
  61. try {stmt.close();}catch (SQLException ingore) {}
  62. }
  63. if (conn != null) {
  64. try {conn.close();}catch (SQLException ingore) {}
  65. }
  66. }
  67. }
  68. public void setTableName(String tableName) {
  69. this.tableName = tableName;
  70. }
  71. }
  72. </pre></code>
  73. * @since Ant 1.5
  74. *
  75. */
  76. public abstract class JDBCTask extends Task {
  77. /**
  78. * Used for caching loaders / driver. This is to avoid
  79. * getting an OutOfMemoryError when calling this task
  80. * multiple times in a row.
  81. */
  82. private static Hashtable loaderMap = new Hashtable(3);
  83. private boolean caching = true;
  84. private Path classpath;
  85. private AntClassLoader loader;
  86. /**
  87. * Autocommit flag. Default value is false
  88. */
  89. private boolean autocommit = false;
  90. /**
  91. * DB driver.
  92. */
  93. private String driver = null;
  94. /**
  95. * DB url.
  96. */
  97. private String url = null;
  98. /**
  99. * User name.
  100. */
  101. private String userId = null;
  102. /**
  103. * Password
  104. */
  105. private String password = null;
  106. /**
  107. * RDBMS Product needed for this SQL.
  108. **/
  109. private String rdbms = null;
  110. /**
  111. * RDBMS Version needed for this SQL.
  112. **/
  113. private String version = null;
  114. /**
  115. * Sets the classpath for loading the driver.
  116. * @param classpath The classpath to set
  117. */
  118. public void setClasspath(Path classpath) {
  119. this.classpath = classpath;
  120. }
  121. /**
  122. * Caching loaders / driver. This is to avoid
  123. * getting an OutOfMemoryError when calling this task
  124. * multiple times in a row; default: true
  125. * @param enable
  126. */
  127. public void setCaching(boolean enable) {
  128. caching = enable;
  129. }
  130. /**
  131. * Add a path to the classpath for loading the driver.
  132. */
  133. public Path createClasspath() {
  134. if (this.classpath == null) {
  135. this.classpath = new Path(getProject());
  136. }
  137. return this.classpath.createPath();
  138. }
  139. /**
  140. * Set the classpath for loading the driver
  141. * using the classpath reference.
  142. */
  143. public void setClasspathRef(Reference r) {
  144. createClasspath().setRefid(r);
  145. }
  146. /**
  147. * Class name of the JDBC driver; required.
  148. * @param driver The driver to set
  149. */
  150. public void setDriver(String driver) {
  151. this.driver = driver;
  152. }
  153. /**
  154. * Sets the database connection URL; required.
  155. * @param url The url to set
  156. */
  157. public void setUrl(String url) {
  158. this.url = url;
  159. }
  160. /**
  161. * Sets the password; required.
  162. * @param password The password to set
  163. */
  164. public void setPassword(String password) {
  165. this.password = password;
  166. }
  167. /**
  168. * Auto commit flag for database connection;
  169. * optional, default false.
  170. * @param autocommit The autocommit to set
  171. */
  172. public void setAutocommit(boolean autocommit) {
  173. this.autocommit = autocommit;
  174. }
  175. /**
  176. * Execute task only if the lower case product name
  177. * of the DB matches this
  178. * @param rdbms The rdbms to set
  179. */
  180. public void setRdbms(String rdbms) {
  181. this.rdbms = rdbms;
  182. }
  183. /**
  184. * Sets the version string, execute task only if
  185. * rdbms version match; optional.
  186. * @param version The version to set
  187. */
  188. public void setVersion(String version) {
  189. this.version = version;
  190. }
  191. /**
  192. * Verify we are connected to the correct RDBMS
  193. */
  194. protected boolean isValidRdbms(Connection conn) {
  195. if (rdbms == null && version == null) {
  196. return true;
  197. }
  198. try {
  199. DatabaseMetaData dmd = conn.getMetaData();
  200. if (rdbms != null) {
  201. String theVendor = dmd.getDatabaseProductName().toLowerCase();
  202. log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
  203. if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
  204. log("Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE);
  205. return false;
  206. }
  207. }
  208. if (version != null) {
  209. // XXX maybe better toLowerCase(Locale.US)
  210. String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
  211. log("Version = " + theVersion, Project.MSG_VERBOSE);
  212. if (theVersion == null
  213. || !(theVersion.startsWith(version)
  214. || theVersion.indexOf(" " + version) >= 0)) {
  215. log("Not the required version: \"" + version + "\"", Project.MSG_VERBOSE);
  216. return false;
  217. }
  218. }
  219. } catch (SQLException e) {
  220. // Could not get the required information
  221. log("Failed to obtain required RDBMS information", Project.MSG_ERR);
  222. return false;
  223. }
  224. return true;
  225. }
  226. protected static Hashtable getLoaderMap() {
  227. return loaderMap;
  228. }
  229. protected AntClassLoader getLoader() {
  230. return loader;
  231. }
  232. /**
  233. * Creates a new Connection as using the driver, url, userid and password
  234. * specified.
  235. *
  236. * The calling method is responsible for closing the connection.
  237. *
  238. * @return Connection the newly created connection.
  239. * @throws BuildException if the UserId/Password/Url is not set or there
  240. * is no suitable driver or the driver fails to load.
  241. */
  242. protected Connection getConnection() throws BuildException {
  243. if (userId == null) {
  244. throw new BuildException("User Id attribute must be set!", getLocation());
  245. }
  246. if (password == null) {
  247. throw new BuildException("Password attribute must be set!", getLocation());
  248. }
  249. if (url == null) {
  250. throw new BuildException("Url attribute must be set!", getLocation());
  251. }
  252. try {
  253. log("connecting to " + getUrl(), Project.MSG_VERBOSE);
  254. Properties info = new Properties();
  255. info.put("user", getUserId());
  256. info.put("password", getPassword());
  257. Connection conn = getDriver().connect(getUrl(), info);
  258. if (conn == null) {
  259. // Driver doesn't understand the URL
  260. throw new SQLException("No suitable Driver for " + url);
  261. }
  262. conn.setAutoCommit(autocommit);
  263. return conn;
  264. } catch (SQLException e) {
  265. throw new BuildException(e, getLocation());
  266. }
  267. }
  268. /**
  269. * Gets an instance of the required driver.
  270. * Uses the ant class loader and the optionally the provided classpath.
  271. * @return Driver
  272. * @throws BuildException
  273. */
  274. private Driver getDriver() throws BuildException {
  275. if (driver == null) {
  276. throw new BuildException("Driver attribute must be set!", getLocation());
  277. }
  278. Driver driverInstance = null;
  279. try {
  280. Class dc;
  281. if (classpath != null) {
  282. // check first that it is not already loaded otherwise
  283. // consecutive runs seems to end into an OutOfMemoryError
  284. // or it fails when there is a native library to load
  285. // several times.
  286. // this is far from being perfect but should work
  287. // in most cases.
  288. synchronized (loaderMap) {
  289. if (caching) {
  290. loader = (AntClassLoader) loaderMap.get(driver);
  291. }
  292. if (loader == null) {
  293. log("Loading " + driver
  294. + " using AntClassLoader with classpath "
  295. + classpath, Project.MSG_VERBOSE);
  296. loader = getProject().createClassLoader(classpath);
  297. if (caching) {
  298. loaderMap.put(driver, loader);
  299. }
  300. } else {
  301. log("Loading " + driver
  302. + " using a cached AntClassLoader.",
  303. Project.MSG_VERBOSE);
  304. }
  305. }
  306. dc = loader.loadClass(driver);
  307. } else {
  308. log("Loading " + driver + " using system loader.",
  309. Project.MSG_VERBOSE);
  310. dc = Class.forName(driver);
  311. }
  312. driverInstance = (Driver) dc.newInstance();
  313. } catch (ClassNotFoundException e) {
  314. throw new BuildException(
  315. "Class Not Found: JDBC driver " + driver + " could not be loaded",
  316. getLocation());
  317. } catch (IllegalAccessException e) {
  318. throw new BuildException(
  319. "Illegal Access: JDBC driver " + driver + " could not be loaded",
  320. getLocation());
  321. } catch (InstantiationException e) {
  322. throw new BuildException(
  323. "Instantiation Exception: JDBC driver " + driver + " could not be loaded",
  324. getLocation());
  325. }
  326. return driverInstance;
  327. }
  328. public void isCaching(boolean value) {
  329. caching = value;
  330. }
  331. /**
  332. * Gets the classpath.
  333. * @return Returns a Path
  334. */
  335. public Path getClasspath() {
  336. return classpath;
  337. }
  338. /**
  339. * Gets the autocommit.
  340. * @return Returns a boolean
  341. */
  342. public boolean isAutocommit() {
  343. return autocommit;
  344. }
  345. /**
  346. * Gets the url.
  347. * @return Returns a String
  348. */
  349. public String getUrl() {
  350. return url;
  351. }
  352. /**
  353. * Gets the userId.
  354. * @return Returns a String
  355. */
  356. public String getUserId() {
  357. return userId;
  358. }
  359. /**
  360. * Set the user name for the connection; required.
  361. * @param userId The userId to set
  362. */
  363. public void setUserid(String userId) {
  364. this.userId = userId;
  365. }
  366. /**
  367. * Gets the password.
  368. * @return Returns a String
  369. */
  370. public String getPassword() {
  371. return password;
  372. }
  373. /**
  374. * Gets the rdbms.
  375. * @return Returns a String
  376. */
  377. public String getRdbms() {
  378. return rdbms;
  379. }
  380. /**
  381. * Gets the version.
  382. * @return Returns a String
  383. */
  384. public String getVersion() {
  385. return version;
  386. }
  387. }