1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.lib.sql;
  58. import java.sql.Connection;
  59. import java.sql.DatabaseMetaData;
  60. import java.sql.DriverManager;
  61. import java.sql.SQLException;
  62. import java.sql.Statement;
  63. import java.sql.PreparedStatement;
  64. import java.sql.ResultSet;
  65. import java.sql.Date;
  66. import java.sql.Timestamp;
  67. import java.sql.Time;
  68. import java.util.Properties;
  69. import java.util.Vector;
  70. import java.util.StringTokenizer;
  71. import java.lang.IllegalArgumentException;
  72. import org.w3c.dom.Element;
  73. import org.w3c.dom.NamedNodeMap;
  74. import org.w3c.dom.NodeList;
  75. import org.w3c.dom.Node;
  76. import java.util.Vector;
  77. import java.util.Enumeration;
  78. import java.math.BigDecimal;
  79. import org.apache.xml.dtm.DTM;
  80. import org.apache.xml.dtm.DTMManager;
  81. import org.apache.xml.dtm.ref.DTMManagerDefault;
  82. import org.apache.xpath.XPathContext;
  83. import org.apache.xalan.extensions.ExpressionContext;
  84. import org.apache.xpath.objects.XBooleanStatic;
  85. import org.w3c.dom.*;
  86. import java.sql.*;
  87. import java.util.*;
  88. /**
  89. * An XSLT extension that allows a stylesheet to
  90. * access JDBC data.
  91. *
  92. * It is accessed by specifying a namespace URI as follows:
  93. * <pre>
  94. * xmlns:sql="http://xml.apache.org/xalan/sql"
  95. * </pre>
  96. *
  97. * From the stylesheet perspective,
  98. * XConnection provides 3 extension functions: new(),
  99. * query(), and close().
  100. * Use new() to call one of XConnection constructors, which
  101. * establishes a JDBC driver connection to a data source and
  102. * returns an XConnection object.
  103. * Then use the XConnection object query() method to return a
  104. * result set in the form of a row-set element.
  105. * When you have finished working with the row-set, call the
  106. * XConnection object close() method to terminate the connection.
  107. */
  108. public class XConnection
  109. {
  110. /**
  111. * Flag for DEBUG mode
  112. */
  113. private static final boolean DEBUG = false;
  114. /**
  115. * The Current Connection Pool in Use. An XConnection can only
  116. * represent one query at a time, prior to doing some type of query.
  117. */
  118. private ConnectionPool m_ConnectionPool = null;
  119. /**
  120. * If a default Connection Pool is used. i.e. A connection Pool
  121. * that is created internally, then do we actually allow pools
  122. * to be created. Due to the archititure of the Xalan Extensions,
  123. * there is no notification of when the Extension is being unloaded and
  124. * as such, there is a good chance that JDBC COnnections are not closed.
  125. * A finalized is provided to try and catch this situation but since
  126. * support of finalizers is inconsistant across JVM's this may cause
  127. * a problem. The robustness of the JDBC Driver is also at issue here.
  128. * if a controlled shutdown is provided by the driver then default
  129. * conntectiom pools are OK.
  130. */
  131. private boolean m_DefaultPoolingEnabled = false;
  132. /**
  133. * As we do queries, we will produce SQL Documents. Any ony may produce
  134. * one or more SQL Documents so that the current connection information
  135. * may be easilly reused. This collection will hold a collection of all
  136. * the documents created. As Documents are closed, they will be removed
  137. * from the collection and told to free all the used resources.
  138. */
  139. private Vector m_OpenSQLDocuments = new Vector();
  140. /**
  141. * Let's keep a copy of the ConnectionPoolMgr in
  142. * alive here so we are keeping the static pool alive
  143. * We will also use this Pool Manager to register our default pools.
  144. */
  145. private ConnectionPoolManager m_PoolMgr = new ConnectionPoolManager();
  146. /**
  147. * For PreparedStatements, we need a place to
  148. * to store the parameters in a vector.
  149. */
  150. private Vector m_ParameterList = new Vector();
  151. /**
  152. * Allow the SQL Extensions to return null on error. The Error information will
  153. * be stored in a seperate Error Document that can easily be retrived using the
  154. * getError() method.
  155. * %REVIEW% This functionality will probably be buried inside the SQLDocument.
  156. */
  157. private SQLErrorDocument m_Error = null;
  158. /**
  159. */
  160. private boolean m_IsDefaultPool = false;
  161. /**
  162. * This flag will be used to indicate to the SQLDocument to use
  163. * Streaming mode. Streeaming Mode will reduce the memory footprint
  164. * to a fixed amount but will not let you traverse the tree more than
  165. * once since the Row data will be reused for every Row in the Query.
  166. */
  167. private boolean m_IsStreamingEnabled = true;
  168. /**
  169. */
  170. public XConnection( )
  171. {
  172. }
  173. // The original constructors will be kept around for backwards
  174. // compatibility. Future Stylesheets should use the approaite
  175. // connect method to receive full error information.
  176. //
  177. /**
  178. * @param exprContext
  179. * @param ConnPoolName
  180. */
  181. public XConnection( ExpressionContext exprContext, String ConnPoolName )
  182. {
  183. connect(exprContext, ConnPoolName);
  184. }
  185. /**
  186. * @param exprContext
  187. * @param driver
  188. * @param dbURL
  189. */
  190. public XConnection( ExpressionContext exprContext, String driver, String dbURL )
  191. {
  192. connect(exprContext, driver, dbURL);
  193. }
  194. /**
  195. * @param exprContext
  196. * @param list
  197. */
  198. public XConnection( ExpressionContext exprContext, NodeList list )
  199. {
  200. connect(exprContext, list);
  201. }
  202. /**
  203. * @param exprContext
  204. * @param driver
  205. * @param dbURL
  206. * @param user
  207. * @param password
  208. */
  209. public XConnection( ExpressionContext exprContext, String driver, String dbURL, String user, String password )
  210. {
  211. connect(exprContext, driver, dbURL, user, password);
  212. }
  213. /**
  214. * @param exprContext
  215. * @param driver
  216. * @param dbURL
  217. * @param protocolElem
  218. */
  219. public XConnection( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem )
  220. {
  221. connect(exprContext, driver, dbURL, protocolElem);
  222. }
  223. /**
  224. * Create an XConnection using the name of an existing Connection Pool
  225. * @param exprContext
  226. * @param ConnPoolName
  227. * @return
  228. */
  229. public XBooleanStatic connect( ExpressionContext exprContext, String ConnPoolName )
  230. {
  231. try
  232. {
  233. m_ConnectionPool = m_PoolMgr.getPool(ConnPoolName);
  234. if (m_ConnectionPool == null)
  235. throw new java.lang.IllegalArgumentException("Invalid Pool Name");
  236. m_IsDefaultPool = false;
  237. return new XBooleanStatic(true);
  238. }
  239. catch (Exception e)
  240. {
  241. buildErrorDocument(exprContext, e);
  242. return new XBooleanStatic(false);
  243. }
  244. }
  245. /**
  246. * Create an XConnection object with just a driver and database URL.
  247. * @param exprContext
  248. * @param driver JDBC driver of the form foo.bar.Driver.
  249. * @param dbURL database URL of the form jdbc:subprotocol:subname.
  250. * @return
  251. */
  252. public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL )
  253. {
  254. try
  255. {
  256. init(driver, dbURL, new Properties());
  257. return new XBooleanStatic(true);
  258. }
  259. catch(SQLException e)
  260. {
  261. buildErrorDocument(exprContext, e);
  262. return new XBooleanStatic(false);
  263. }
  264. catch (Exception e)
  265. {
  266. buildErrorDocument(exprContext, e);
  267. return new XBooleanStatic(false);
  268. }
  269. }
  270. /**
  271. * @param exprContext
  272. * @param protocolElem
  273. * @return
  274. */
  275. public XBooleanStatic connect( ExpressionContext exprContext, Element protocolElem )
  276. {
  277. try
  278. {
  279. initFromElement(protocolElem);
  280. return new XBooleanStatic(true);
  281. }
  282. catch(SQLException e)
  283. {
  284. buildErrorDocument(exprContext, e);
  285. return new XBooleanStatic(false);
  286. }
  287. catch (Exception e)
  288. {
  289. buildErrorDocument(exprContext, e);
  290. return new XBooleanStatic(false);
  291. }
  292. }
  293. /**
  294. * @param exprContext
  295. * @param list
  296. * @return
  297. */
  298. public XBooleanStatic connect( ExpressionContext exprContext, NodeList list )
  299. {
  300. try
  301. {
  302. initFromElement( (Element) list.item(0) );
  303. return new XBooleanStatic(true);
  304. }
  305. catch(SQLException e)
  306. {
  307. buildErrorDocument(exprContext, e);
  308. return new XBooleanStatic(false);
  309. }
  310. catch (Exception e)
  311. {
  312. buildErrorDocument(exprContext, e);
  313. return new XBooleanStatic(false);
  314. }
  315. }
  316. /**
  317. * Create an XConnection object with user ID and password.
  318. * @param exprContext
  319. * @param driver JDBC driver of the form foo.bar.Driver.
  320. * @param dbURL database URL of the form jdbc:subprotocol:subname.
  321. * @param user user ID.
  322. * @param password connection password.
  323. * @return
  324. */
  325. public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, String user, String password )
  326. {
  327. try
  328. {
  329. Properties prop = new Properties();
  330. prop.put("user", user);
  331. prop.put("password", password);
  332. init(driver, dbURL, prop);
  333. return new XBooleanStatic(true);
  334. }
  335. catch(SQLException e)
  336. {
  337. buildErrorDocument(exprContext, e);
  338. return new XBooleanStatic(false);
  339. }
  340. catch (Exception e)
  341. {
  342. buildErrorDocument(exprContext, e);
  343. return new XBooleanStatic(false);
  344. }
  345. }
  346. /**
  347. * Create an XConnection object with a connection protocol
  348. * @param exprContext
  349. * @param driver JDBC driver of the form foo.bar.Driver.
  350. * @param dbURL database URL of the form jdbc:subprotocol:subname.
  351. * @param protocolElem list of string tag/value connection arguments,
  352. * normally including at least "user" and "password".
  353. * @return
  354. */
  355. public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem )
  356. {
  357. try
  358. {
  359. Properties prop = new Properties();
  360. NamedNodeMap atts = protocolElem.getAttributes();
  361. for (int i = 0; i < atts.getLength(); i++)
  362. {
  363. prop.put(atts.item(i).getNodeName(), atts.item(i).getNodeValue());
  364. }
  365. init(driver, dbURL, prop);
  366. return new XBooleanStatic(true);
  367. }
  368. catch(SQLException e)
  369. {
  370. buildErrorDocument(exprContext, e);
  371. return new XBooleanStatic(false);
  372. }
  373. catch (Exception e)
  374. {
  375. buildErrorDocument(exprContext, e);
  376. return new XBooleanStatic(false);
  377. }
  378. }
  379. /**
  380. * Allow the database connection information to be sepcified in
  381. * the XML tree. The connection information could also be
  382. * externally originated and passed in as an XSL Parameter.
  383. * The required XML Format is as follows.
  384. * A document fragment is needed to specify the connection information
  385. * the top tag name is not specific for this code, we are only interested
  386. * in the tags inside.
  387. * <DBINFO-TAG>
  388. * Specify the driver name for this connection pool
  389. * <dbdriver>drivername</dbdriver>
  390. * Specify the URL for the driver in this connection pool
  391. * <dburl>url</dburl>
  392. * Specify the password for this connection pool
  393. * <password>password</password>
  394. * Specify the username for this connection pool
  395. * <user>username</user>
  396. * You can add extra protocol items including the User Name & Password
  397. * with the protocol tag. For each extra protocol item, add a new element
  398. * where the name of the item is specified as the name attribute and
  399. * and its value as the elements value.
  400. * <protocol name="name of value">value</protocol>
  401. * </DBINFO-TAG>
  402. * @param e
  403. * @return
  404. * @throws SQLException
  405. */
  406. private void initFromElement( Element e )throws SQLException
  407. {
  408. Properties prop = new Properties();
  409. String driver = "";
  410. String dbURL = "";
  411. Node n = e.getFirstChild();
  412. if (null == n) return; // really need to throw an error
  413. do
  414. {
  415. String nName = n.getNodeName();
  416. if (nName.equalsIgnoreCase("dbdriver"))
  417. {
  418. driver = "";
  419. Node n1 = n.getFirstChild();
  420. if (null != n1)
  421. {
  422. driver = n1.getNodeValue();
  423. }
  424. }
  425. if (nName.equalsIgnoreCase("dburl"))
  426. {
  427. dbURL = "";
  428. Node n1 = n.getFirstChild();
  429. if (null != n1)
  430. {
  431. dbURL = n1.getNodeValue();
  432. }
  433. }
  434. if (nName.equalsIgnoreCase("password"))
  435. {
  436. String s = "";
  437. Node n1 = n.getFirstChild();
  438. if (null != n1)
  439. {
  440. s = n1.getNodeValue();
  441. }
  442. prop.put("password", s);
  443. }
  444. if (nName.equalsIgnoreCase("user"))
  445. {
  446. String s = "";
  447. Node n1 = n.getFirstChild();
  448. if (null != n1)
  449. {
  450. s = n1.getNodeValue();
  451. }
  452. prop.put("user", s);
  453. }
  454. if (nName.equalsIgnoreCase("protocol"))
  455. {
  456. String Name = "";
  457. NamedNodeMap attrs = n.getAttributes();
  458. Node n1 = attrs.getNamedItem("name");
  459. if (null != n1)
  460. {
  461. String s = "";
  462. Name = n1.getNodeValue();
  463. Node n2 = n.getFirstChild();
  464. if (null != n2) s = n2.getNodeValue();
  465. prop.put(Name, s);
  466. }
  467. }
  468. } while ( (n = n.getNextSibling()) != null);
  469. init(driver, dbURL, prop);
  470. }
  471. /**
  472. * Initilize is being called because we did not have an
  473. * existing Connection Pool, so let's see if we created one
  474. * already or lets create one ourselves.
  475. * @param driver
  476. * @param dbURL
  477. * @param prop
  478. * @return
  479. * @throws SQLException
  480. */
  481. private void init( String driver, String dbURL, Properties prop )throws SQLException
  482. {
  483. Connection con = null;
  484. if (DEBUG)
  485. System.out.println("XConnection, Connection Init");
  486. String user = prop.getProperty("user");
  487. if (user == null) user = "";
  488. String passwd = prop.getProperty("password");
  489. if (passwd == null) passwd = "";
  490. String poolName = driver + dbURL + user + passwd;
  491. ConnectionPool cpool = m_PoolMgr.getPool(poolName);
  492. if (cpool == null)
  493. {
  494. if (DEBUG)
  495. {
  496. System.out.println("XConnection, Creating Connection");
  497. System.out.println(" Driver :" + driver);
  498. System.out.println(" URL :" + dbURL);
  499. System.out.println(" user :" + user);
  500. System.out.println(" passwd :" + passwd);
  501. }
  502. DefaultConnectionPool defpool = new DefaultConnectionPool();
  503. if ((DEBUG) && (defpool == null))
  504. System.out.println("Failed to Create a Default Connection Pool");
  505. defpool.setDriver(driver);
  506. defpool.setURL(dbURL);
  507. defpool.setProtocol(prop);
  508. // Only enable pooling in the default pool if we are explicatly
  509. // told too.
  510. if (m_DefaultPoolingEnabled) defpool.setPoolEnabled(true);
  511. m_PoolMgr.registerPool(poolName, defpool);
  512. m_ConnectionPool = defpool;
  513. }
  514. else
  515. {
  516. m_ConnectionPool = cpool;
  517. }
  518. m_IsDefaultPool = true;
  519. //
  520. // Let's test to see if we really can connect
  521. // Just remember to give it back after the test.
  522. //
  523. try
  524. {
  525. con = m_ConnectionPool.getConnection();
  526. }
  527. catch(SQLException e)
  528. {
  529. if (con != null)
  530. {
  531. m_ConnectionPool.releaseConnectionOnError(con);
  532. con = null;
  533. }
  534. throw e;
  535. }
  536. finally
  537. {
  538. m_ConnectionPool.releaseConnection(con);
  539. }
  540. }
  541. /**
  542. * Execute a query statement by instantiating an
  543. * @param exprContext
  544. * @param queryString the SQL query.
  545. * @return XStatement implements NodeIterator.
  546. * @throws SQLException
  547. * @link org.apache.xalan.lib.sql.XStatement XStatement}
  548. * object. The XStatement executes the query, and uses the result set
  549. * to create a
  550. * @link org.apache.xalan.lib.sql.RowSet RowSet},
  551. * a row-set element.
  552. */
  553. public DTM query( ExpressionContext exprContext, String queryString )
  554. {
  555. Connection con = null;
  556. Statement stmt = null;
  557. ResultSet rs = null;
  558. DTMManagerDefault mgrDefault = null;
  559. SQLDocument doc = null;
  560. try
  561. {
  562. if (DEBUG) System.out.println("query()");
  563. if (null == m_ConnectionPool)
  564. {
  565. // Build an Error Document, NOT Connected
  566. return null;
  567. }
  568. try
  569. {
  570. con = m_ConnectionPool.getConnection();
  571. stmt = con.createStatement();
  572. rs = stmt.executeQuery(queryString);
  573. }
  574. catch(SQLException e)
  575. {
  576. // We have not created a document yet, so lets close the
  577. // connection ourselves then let the process deal with the
  578. // error.
  579. //
  580. try { if (null != rs) rs.close(); }
  581. catch(Exception e1) {}
  582. try { if (null != stmt) stmt.close(); }
  583. catch(Exception e1) { }
  584. try {
  585. if (null != con) m_ConnectionPool.releaseConnectionOnError(con);
  586. } catch(Exception e1) { }
  587. buildErrorDocument(exprContext, e);
  588. return null;
  589. }
  590. if (DEBUG) System.out.println("..creatingSQLDocument");
  591. DTMManager mgr =
  592. ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  593. mgrDefault = (DTMManagerDefault) mgr;
  594. int dtmIdent = mgrDefault.getFirstFreeDTMID();
  595. doc =
  596. new SQLDocument(
  597. mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS ,
  598. m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled);
  599. if (null != doc)
  600. {
  601. if (DEBUG) System.out.println("..returning Document");
  602. // Register our document
  603. mgrDefault.addDTM(doc, dtmIdent);
  604. // also keep a local reference
  605. m_OpenSQLDocuments.addElement(doc);
  606. return doc;
  607. }
  608. else
  609. {
  610. return null;
  611. }
  612. }
  613. catch(SQLException e)
  614. {
  615. if ((doc != null) && (mgrDefault != null))
  616. {
  617. doc.closeOnError();
  618. mgrDefault.release(doc, true);
  619. }
  620. buildErrorDocument(exprContext, e);
  621. return null;
  622. }
  623. catch (Exception e)
  624. {
  625. if ((doc != null) && (mgrDefault != null))
  626. {
  627. doc.closeOnError();
  628. mgrDefault.release(doc, true);
  629. }
  630. if (DEBUG) System.out.println("exception in query()");
  631. buildErrorDocument(exprContext, e);
  632. return null;
  633. }
  634. finally
  635. {
  636. if (DEBUG) System.out.println("leaving query()");
  637. }
  638. }
  639. /**
  640. * Execute a parameterized query statement by instantiating an
  641. * @param exprContext
  642. * @param queryString the SQL query.
  643. * @return XStatement implements NodeIterator.
  644. * @throws SQLException
  645. * @link org.apache.xalan.lib.sql.XStatement XStatement}
  646. * object. The XStatement executes the query, and uses the result set
  647. * to create a
  648. * @link org.apache.xalan.lib.sql.RowSet RowSet},
  649. * a row-set element.
  650. */
  651. public DTM pquery( ExpressionContext exprContext, String queryString )
  652. {
  653. Connection con = null;
  654. PreparedStatement stmt = null;
  655. ResultSet rs = null;
  656. try
  657. {
  658. int indx;
  659. try
  660. {
  661. con = m_ConnectionPool.getConnection();
  662. stmt = con.prepareStatement(queryString);
  663. }
  664. catch(SQLException e)
  665. {
  666. // We have not created a document yet, so lets close the
  667. // connection ourselves then let the process deal with the
  668. // error.
  669. //
  670. try { if (null != stmt) stmt.close(); }
  671. catch(Exception e1) { }
  672. try {
  673. if (null != con) m_ConnectionPool.releaseConnectionOnError(con);
  674. } catch(Exception e1) {}
  675. // Re throw the error so the process can handle the error
  676. // normally
  677. throw e;
  678. }
  679. if (DEBUG) System.out.println("..building Prepared Statement");
  680. try
  681. {
  682. Enumeration enum = m_ParameterList.elements();
  683. indx = 1;
  684. while (enum.hasMoreElements())
  685. {
  686. QueryParameter qp = (QueryParameter) enum.nextElement();
  687. setParameter(indx, stmt, qp);
  688. indx++;
  689. }
  690. rs = stmt.executeQuery();
  691. }
  692. catch(SQLException e)
  693. {
  694. // We have not created a document yet, so lets close the
  695. // connection ourselves then let the process deal with the
  696. // error.
  697. //
  698. try { if (null != rs) rs.close(); }
  699. catch(Exception e1) { }
  700. try { if (null != stmt) stmt.close(); }
  701. catch(Exception e1) { }
  702. try {
  703. if (null != con) m_ConnectionPool.releaseConnectionOnError(con);
  704. } catch(Exception e1) { }
  705. // Re throw the error so the process can handle the error
  706. // normally
  707. throw e;
  708. }
  709. if (DEBUG) System.out.println("..creatingSQLDocument");
  710. DTMManager mgr =
  711. ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  712. DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr;
  713. int dtmIdent = mgrDefault.getFirstFreeDTMID();
  714. SQLDocument doc =
  715. new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS,
  716. m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled);
  717. if (null != doc)
  718. {
  719. if (DEBUG) System.out.println("..returning Document");
  720. // Register our document
  721. mgrDefault.addDTM(doc, dtmIdent);
  722. // also keep a local reference
  723. m_OpenSQLDocuments.addElement(doc);
  724. return doc;
  725. }
  726. else
  727. {
  728. // Build Error Doc, BAD Result Set
  729. return null;
  730. }
  731. }
  732. catch(SQLException e)
  733. {
  734. buildErrorDocument(exprContext, e);
  735. return null;
  736. }
  737. catch (Exception e)
  738. {
  739. buildErrorDocument(exprContext, e);
  740. return null;
  741. }
  742. }
  743. /**
  744. * Execute a parameterized query statement by instantiating an
  745. * @param exprContext
  746. * @param queryString the SQL query.
  747. * @param typeInfo
  748. * @return XStatement implements NodeIterator.
  749. * @throws SQLException
  750. * @link org.apache.xalan.lib.sql.XStatement XStatement}
  751. * object. The XStatement executes the query, and uses the result set
  752. * to create a
  753. * @link org.apache.xalan.lib.sql.RowSet RowSet},
  754. * a row-set element.
  755. * This method allows for the user to pass in a comma seperated
  756. * String that represents a list of parameter types. If supplied
  757. * the parameter types will be used to overload the current types
  758. * in the current parameter list.
  759. */
  760. public DTM pquery( ExpressionContext exprContext, String queryString, String typeInfo )
  761. {
  762. Connection con = null;
  763. PreparedStatement stmt = null;
  764. ResultSet rs = null;
  765. try
  766. {
  767. int indx;
  768. // Parse up the parameter types that were defined
  769. // with the query
  770. StringTokenizer plist = new StringTokenizer(typeInfo);
  771. // Override the existing type that is stored in the
  772. // parameter list. If there are more types than parameters
  773. // ignore for now, a more meaningfull error should occur
  774. // when the actual query is executed.
  775. indx = 0;
  776. while (plist.hasMoreTokens())
  777. {
  778. String value = plist.nextToken();
  779. QueryParameter qp = (QueryParameter) m_ParameterList.elementAt(indx);
  780. if ( null != qp )
  781. {
  782. qp.setType(value);
  783. }
  784. indx++;
  785. }
  786. try
  787. {
  788. con = m_ConnectionPool.getConnection();
  789. stmt = con.prepareStatement(queryString);
  790. }
  791. catch(SQLException e)
  792. {
  793. // We have not created a document yet, so lets close the
  794. // connection ourselves then let the process deal with the
  795. // error.
  796. //
  797. try { if (null != stmt) stmt.close(); }
  798. catch(Exception e1) { }
  799. try {
  800. if (null != con) m_ConnectionPool.releaseConnectionOnError(con);
  801. } catch(Exception e1) { }
  802. // Re throw the error so the process can handle the error
  803. // normally
  804. throw e;
  805. }
  806. if (DEBUG) System.out.println("..building Prepared Statement");
  807. try
  808. {
  809. Enumeration enum = m_ParameterList.elements();
  810. indx = 1;
  811. while (enum.hasMoreElements())
  812. {
  813. QueryParameter qp = (QueryParameter) enum.nextElement();
  814. setParameter(indx, stmt, qp);
  815. indx++;
  816. }
  817. rs = stmt.executeQuery();
  818. }
  819. catch(SQLException e)
  820. {
  821. // We have not created a document yet, so lets close the
  822. // connection ourselves then let the process deal with the
  823. // error.
  824. //
  825. try { if (null != rs) rs.close(); }
  826. catch(Exception e1) { /* Empty */ }
  827. try { if (null != stmt) stmt.close(); }
  828. catch(Exception e1) { /* Empty */ }
  829. try {
  830. if (null != con) m_ConnectionPool.releaseConnectionOnError(con);
  831. } catch(Exception e1) { /* Empty */ }
  832. // Re throw the error so the process can handle the error
  833. // normally
  834. throw e;
  835. }
  836. if (DEBUG) System.out.println("..creatingSQLDocument");
  837. DTMManager mgr =
  838. ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  839. DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr;
  840. int dtmIdent = mgrDefault.getFirstFreeDTMID();
  841. SQLDocument doc =
  842. new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS ,
  843. m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled);
  844. if (null != doc)
  845. {
  846. if (DEBUG) System.out.println("..returning Document");
  847. // Register our document
  848. mgrDefault.addDTM(doc, dtmIdent);
  849. // also keep a local reference
  850. m_OpenSQLDocuments.addElement(doc);
  851. return doc;
  852. }
  853. else
  854. {
  855. // Build Error Doc, BAD Result Set
  856. return null;
  857. }
  858. }
  859. catch(SQLException e)
  860. {
  861. buildErrorDocument(exprContext, e);
  862. return null;
  863. }
  864. catch (Exception e)
  865. {
  866. buildErrorDocument(exprContext, e);
  867. return null;
  868. }
  869. }
  870. /**
  871. * Add an untyped value to the parameter list.
  872. * @param value
  873. * @return
  874. */
  875. public void addParameter( String value )
  876. {
  877. addParameterWithType(value, null);
  878. }
  879. /**
  880. * Add a typed parameter to the parameter list.
  881. * @param value
  882. * @param Type
  883. * @return
  884. */
  885. public void addParameterWithType( String value, String Type )
  886. {
  887. m_ParameterList.addElement( new QueryParameter(value, Type) );
  888. }
  889. /**
  890. * Add a single parameter to the parameter list
  891. * formatted as an Element
  892. * @param e
  893. * @return
  894. */
  895. public void addParameterFromElement( Element e )
  896. {
  897. NamedNodeMap attrs = e.getAttributes();
  898. Node Type = attrs.getNamedItem("type");
  899. Node n1 = e.getFirstChild();
  900. if (null != n1)
  901. {
  902. String value = n1.getNodeValue();
  903. if (value == null) value = "";
  904. m_ParameterList.addElement( new QueryParameter(value, Type.getNodeValue()) );
  905. }
  906. }
  907. /**
  908. * Add a section of parameters to the Parameter List
  909. * Do each element from the list
  910. * @param nl
  911. * @return
  912. */
  913. public void addParameterFromElement( NodeList nl )
  914. {
  915. //
  916. // Each child of the NodeList represents a node
  917. // match from the select= statment. Process each
  918. // of them as a seperate list.
  919. // The XML Format is as follows
  920. //
  921. // <START-TAG>
  922. // <TAG1 type="int">value</TAG1>
  923. // <TAGA type="int">value</TAGA>
  924. // <TAG2 type="string">value</TAG2>
  925. // </START-TAG>
  926. //
  927. // The XSL to process this is formatted as follows
  928. // <xsl:param name="plist" select="//START-TAG" />
  929. // <sql:addParameter( $plist );
  930. //
  931. int count = nl.getLength();
  932. for (int x=0; x<count; x++)
  933. {
  934. addParameters( (Element) nl.item(x));
  935. }
  936. }
  937. /**
  938. * @param elem
  939. * @return
  940. */
  941. private void addParameters( Element elem )
  942. {
  943. //
  944. // Process all of the Child Elements
  945. // The format is as follows
  946. //
  947. //<TAG type ="typeid">value</TAG>
  948. //<TAG1 type ="typeid">value</TAG1>
  949. //<TAGA type ="typeid">value</TAGA>
  950. //
  951. // The name of the Node is not important just is value
  952. // and if it contains a type attribute
  953. Node n = elem.getFirstChild();
  954. if (null == n) return;
  955. do
  956. {
  957. if (n.getNodeType() == Node.ELEMENT_NODE)
  958. {
  959. NamedNodeMap attrs = n.getAttributes();
  960. Node Type = attrs.getNamedItem("type");
  961. String TypeStr;
  962. if (Type == null) TypeStr = "string";
  963. else TypeStr = Type.getNodeValue();
  964. Node n1 = n.getFirstChild();
  965. if (null != n1)
  966. {
  967. String value = n1.getNodeValue();
  968. if (value == null) value = "";
  969. m_ParameterList.addElement(
  970. new QueryParameter(value, TypeStr) );
  971. }
  972. }
  973. } while ( (n = n.getNextSibling()) != null);
  974. }
  975. /**
  976. * @return
  977. */
  978. public void clearParameters( )
  979. {
  980. m_ParameterList.removeAllElements();
  981. }
  982. /**
  983. * There is a problem with some JDBC drivers when a Connection
  984. * is open and the JVM shutsdown. If there is a problem, there
  985. * is no way to control the currently open connections in the
  986. * pool. So for the default connection pool, the actuall pooling
  987. * mechinsm is disabled by default. The Stylesheet designer can
  988. * re-enabled pooling to take advantage of connection pools.
  989. * The connection pool can even be disabled which will close all
  990. * outstanding connections.
  991. * @return
  992. */
  993. public void enableDefaultConnectionPool( )
  994. {
  995. if (DEBUG)
  996. System.out.println("Enabling Default Connection Pool");
  997. m_DefaultPoolingEnabled = true;
  998. if (m_ConnectionPool == null) return;
  999. if (m_IsDefaultPool) return;
  1000. m_ConnectionPool.setPoolEnabled(true);
  1001. }
  1002. /**
  1003. * See enableDefaultConnectionPool
  1004. * @return
  1005. */
  1006. public void disableDefaultConnectionPool( )
  1007. {
  1008. if (DEBUG)
  1009. System.out.println("Disabling Default Connection Pool");
  1010. m_DefaultPoolingEnabled = false;
  1011. if (m_ConnectionPool == null) return;
  1012. if (!m_IsDefaultPool) return;
  1013. m_ConnectionPool.setPoolEnabled(false);
  1014. }
  1015. /**
  1016. * Control how the SQL Document uses memory. In Streaming Mode,
  1017. * memory consumption is greatly reduces so you can have queries
  1018. * of unlimited size but it will not let you traverse the data
  1019. * more than once.
  1020. * @return
  1021. */
  1022. public void enableStreamingMode( )
  1023. {
  1024. if (DEBUG)
  1025. System.out.println("Enabling Streaming Mode");
  1026. m_IsStreamingEnabled = true;
  1027. }
  1028. /**
  1029. * Control how the SQL Document uses memory. In Streaming Mode,
  1030. * memory consumption is greatly reduces so you can have queries
  1031. * of unlimited size but it will not let you traverse the data
  1032. * more than once.
  1033. * @return
  1034. */
  1035. public void disableStreamingMode( )
  1036. {
  1037. if (DEBUG)
  1038. System.out.println("Disable Streaming Mode");
  1039. m_IsStreamingEnabled = false;
  1040. }
  1041. /**
  1042. * Provide access to the last error that occued. This error
  1043. * may be over written when the next operation occurs.
  1044. * @return
  1045. */
  1046. public DTM getError( )
  1047. {
  1048. return m_Error;
  1049. }
  1050. /**
  1051. * Close the connection to the data source.
  1052. * @return
  1053. * @throws SQLException
  1054. */
  1055. public void close( )throws SQLException
  1056. {
  1057. if (DEBUG)
  1058. System.out.println("Entering XConnection.close");
  1059. //
  1060. // This function is included just for Legacy support
  1061. // If it is really called then we must me using a single
  1062. // document interface, so close all open documents.
  1063. while(m_OpenSQLDocuments.size() != 0)
  1064. {
  1065. SQLDocument d = (SQLDocument) m_OpenSQLDocuments.elementAt(0);
  1066. d.close();
  1067. m_OpenSQLDocuments.removeElementAt(0);
  1068. }
  1069. if (DEBUG)
  1070. System.out.println("Exiting XConnection.close");
  1071. }
  1072. /**
  1073. * Close the connection to the data source. Only close the connections
  1074. * for a single document.
  1075. * @param sqldoc
  1076. * @return
  1077. * @throws SQLException
  1078. */
  1079. public void close( SQLDocument sqldoc )throws SQLException
  1080. {
  1081. if (DEBUG)
  1082. System.out.println("Entering XConnection.close");
  1083. int size = m_OpenSQLDocuments.size();
  1084. for(int x=0; x<size; x++)
  1085. {
  1086. SQLDocument d = (SQLDocument) m_OpenSQLDocuments.elementAt(x);
  1087. if (d == sqldoc)
  1088. {
  1089. d.close();
  1090. m_OpenSQLDocuments.removeElementAt(x);
  1091. }
  1092. }
  1093. }
  1094. /**
  1095. * Set the parameter for a Prepared Statement
  1096. * @param pos
  1097. * @param stmt
  1098. * @param p
  1099. * @return
  1100. * @throws SQLException
  1101. */
  1102. public void setParameter( int pos, PreparedStatement stmt, QueryParameter p )throws SQLException
  1103. {
  1104. String type = p.getType();
  1105. if (type.equalsIgnoreCase("string"))
  1106. {
  1107. stmt.setString(pos, p.getValue());
  1108. }
  1109. if (type.equalsIgnoreCase("bigdecimal"))
  1110. {
  1111. stmt.setBigDecimal(pos, new BigDecimal(p.getValue()));
  1112. }
  1113. if (type.equalsIgnoreCase("boolean"))
  1114. {
  1115. Integer i = new Integer( p.getValue() );
  1116. boolean b = ((i.intValue() != 0) ? false : true);
  1117. stmt.setBoolean(pos, b);
  1118. }
  1119. if (type.equalsIgnoreCase("bytes"))
  1120. {
  1121. stmt.setBytes(pos, p.getValue().getBytes());
  1122. }
  1123. if (type.equalsIgnoreCase("date"))
  1124. {
  1125. stmt.setDate(pos, Date.valueOf(p.getValue()));
  1126. }
  1127. if (type.equalsIgnoreCase("double"))
  1128. {
  1129. Double d = new Double(p.getValue());
  1130. stmt.setDouble(pos, d.doubleValue() );
  1131. }
  1132. if (type.equalsIgnoreCase("float"))
  1133. {
  1134. Float f = new Float(p.getValue());
  1135. stmt.setFloat(pos, f.floatValue());
  1136. }
  1137. if (type.equalsIgnoreCase("long"))
  1138. {
  1139. Long l = new Long(p.getValue());
  1140. stmt.setLong(pos, l.longValue());
  1141. }
  1142. if (type.equalsIgnoreCase("short"))
  1143. {
  1144. Short s = new Short(p.getValue());
  1145. stmt.setShort(pos, s.shortValue());
  1146. }
  1147. if (type.equalsIgnoreCase("time"))
  1148. {
  1149. stmt.setTime(pos, Time.valueOf(p.getValue()) );
  1150. }
  1151. if (type.equalsIgnoreCase("timestamp"))
  1152. {
  1153. stmt.setTimestamp(pos, Timestamp.valueOf(p.getValue()) );
  1154. }
  1155. }
  1156. /**
  1157. * @param exprContext
  1158. * @param excp
  1159. * @return
  1160. */
  1161. private void buildErrorDocument( ExpressionContext exprContext, SQLException excp )
  1162. {
  1163. try
  1164. {
  1165. DTMManager mgr =
  1166. ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  1167. DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr;
  1168. int dtmIdent = mgrDefault.getFirstFreeDTMID();
  1169. m_Error = new SQLErrorDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS, excp);
  1170. // Register our document
  1171. mgrDefault.addDTM(m_Error, dtmIdent);
  1172. }
  1173. catch(Exception e)
  1174. {
  1175. m_Error = null;
  1176. }
  1177. }
  1178. /**
  1179. * @param exprContext
  1180. * @param excp
  1181. * @return
  1182. */
  1183. private void buildErrorDocument( ExpressionContext exprContext, Exception excp )
  1184. {
  1185. try
  1186. {
  1187. DTMManager mgr =
  1188. ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  1189. DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr;
  1190. int dtmIdent = mgrDefault.getFirstFreeDTMID();
  1191. m_Error = new SQLErrorDocument(mgr, dtmIdent<<DTMManager.IDENT_DTM_NODE_BITS, excp);
  1192. // Register our document
  1193. mgrDefault.addDTM(m_Error, dtmIdent);
  1194. }
  1195. catch(Exception e)
  1196. {
  1197. m_Error = null;
  1198. }
  1199. }
  1200. /**
  1201. * @return
  1202. */
  1203. protected void finalize( )
  1204. {
  1205. if (DEBUG) System.out.println("In XConnection, finalize");
  1206. try
  1207. {
  1208. close();
  1209. }
  1210. catch(Exception e)
  1211. {
  1212. // Empty We are final Anyway
  1213. }
  1214. }
  1215. }