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

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