| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, Lotus |
| * Development Corporation., http://www.lotus.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.xalan.lib.sql; |
| |
| import java.sql.Connection; |
| import java.sql.DatabaseMetaData; |
| import java.sql.DriverManager; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.Date; |
| import java.sql.Timestamp; |
| import java.sql.Time; |
| import java.util.Properties; |
| import java.util.Vector; |
| import java.util.StringTokenizer; |
| import java.lang.IllegalArgumentException; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.Node; |
| import java.util.Vector; |
| import java.util.Enumeration; |
| import java.math.BigDecimal; |
| import org.apache.xml.dtm.DTM; |
| import org.apache.xml.dtm.DTMManager; |
| import org.apache.xml.dtm.ref.DTMManagerDefault; |
| import org.apache.xpath.XPathContext; |
| import org.apache.xalan.extensions.ExpressionContext; |
| import org.apache.xpath.objects.XBooleanStatic; |
| |
| import org.w3c.dom.*; |
| import java.sql.*; |
| import java.util.*; |
| |
| /** |
| * An XSLT extension that allows a stylesheet to |
| * access JDBC data. From the stylesheet perspective, |
| * XConnection provides 3 extension functions: new(), |
| * query(), and close(). |
| * Use new() to call one of XConnection constructors, which |
| * establishes a JDBC driver connection to a data source and |
| * returns an XConnection object. |
| * Then use the XConnection object query() method to return a |
| * result set in the form of a row-set element. |
| * When you have finished working with the row-set, call the |
| * XConnection object close() method to terminate the connection. |
| */ |
| public class XConnection |
| { |
| |
| /** |
| * Flag for DEBUG mode |
| */ |
| private static final boolean DEBUG = false; |
| |
| /** |
| * The Current Connection Pool in Use. An XConnection can only |
| * represent one query at a time, prior to doing some type of query. |
| */ |
| private ConnectionPool m_ConnectionPool = null; |
| |
| /** |
| * If a default Connection Pool is used. i.e. A connection Pool |
| * that is created internally, then do we actually allow pools |
| * to be created. Due to the archititure of the Xalan Extensions, |
| * there is no notification of when the Extension is being unloaded and |
| * as such, there is a good chance that JDBC COnnections are not closed. |
| * A finalized is provided to try and catch this situation but since |
| * support of finalizers is inconsistant across JVM's this may cause |
| * a problem. The robustness of the JDBC Driver is also at issue here. |
| * if a controlled shutdown is provided by the driver then default |
| * conntectiom pools are OK. |
| */ |
| private boolean m_DefaultPoolingEnabled = false; |
| |
| |
| /** |
| * As we do queries, we will produce SQL Documents. Any ony may produce |
| * one or more SQL Documents so that the current connection information |
| * may be easilly reused. This collection will hold a collection of all |
| * the documents created. As Documents are closed, they will be removed |
| * from the collection and told to free all the used resources. |
| */ |
| private Vector m_OpenSQLDocuments = new Vector(); |
| |
| |
| /** |
| * Let's keep a copy of the ConnectionPoolMgr in |
| * alive here so we are keeping the static pool alive |
| * We will also use this Pool Manager to register our default pools. |
| */ |
| private ConnectionPoolManager m_PoolMgr = new ConnectionPoolManager(); |
| |
| /** |
| * For PreparedStatements, we need a place to |
| * to store the parameters in a vector. |
| */ |
| private Vector m_ParameterList = new Vector(); |
| |
| /** |
| * Allow the SQL Extensions to return null on error. The Error information will |
| * be stored in a seperate Error Document that can easily be retrived using the |
| * getError() method. |
| * %REVIEW% This functionality will probably be buried inside the SQLDocument. |
| */ |
| private SQLErrorDocument m_Error = null; |
| |
| /** |
| */ |
| private boolean m_IsDefaultPool = false; |
| |
| /** |
| * This flag will be used to indicate to the SQLDocument to use |
| * Streaming mode. Streeaming Mode will reduce the memory footprint |
| * to a fixed amount but will not let you traverse the tree more than |
| * once since the Row data will be reused for every Row in the Query. |
| */ |
| private boolean m_IsStreamingEnabled = true; |
| |
| /** |
| */ |
| public XConnection( ) |
| { |
| } |
| |
| // The original constructors will be kept around for backwards |
| // compatibility. Future Stylesheets should use the approaite |
| // connect method to receive full error information. |
| // |
| /** |
| * @param exprContext |
| * @param ConnPoolName |
| */ |
| public XConnection( ExpressionContext exprContext, String ConnPoolName ) |
| { |
| connect(exprContext, ConnPoolName); |
| } |
| |
| /** |
| * @param exprContext |
| * @param driver |
| * @param dbURL |
| */ |
| public XConnection( ExpressionContext exprContext, String driver, String dbURL ) |
| { |
| connect(exprContext, driver, dbURL); |
| } |
| |
| /** |
| * @param exprContext |
| * @param list |
| */ |
| public XConnection( ExpressionContext exprContext, NodeList list ) |
| { |
| connect(exprContext, list); |
| } |
| |
| /** |
| * @param exprContext |
| * @param driver |
| * @param dbURL |
| * @param user |
| * @param password |
| */ |
| public XConnection( ExpressionContext exprContext, String driver, String dbURL, String user, String password ) |
| { |
| connect(exprContext, driver, dbURL, user, password); |
| } |
| |
| /** |
| * @param exprContext |
| * @param driver |
| * @param dbURL |
| * @param protocolElem |
| */ |
| public XConnection( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem ) |
| { |
| connect(exprContext, driver, dbURL, protocolElem); |
| } |
| |
| |
| /** |
| * Create an XConnection using the name of an existing Connection Pool |
| * @param exprContext |
| * @param ConnPoolName |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, String ConnPoolName ) |
| { |
| try |
| { |
| m_ConnectionPool = m_PoolMgr.getPool(ConnPoolName); |
| |
| if (m_ConnectionPool == null) |
| throw new java.lang.IllegalArgumentException("Invalid Pool Name"); |
| |
| m_IsDefaultPool = false; |
| return new XBooleanStatic(true); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| |
| } |
| |
| /** |
| * Create an XConnection object with just a driver and database URL. |
| * @param exprContext |
| * @param driver JDBC driver of the form foo.bar.Driver. |
| * @param dbURL database URL of the form jdbc:subprotocol:subname. |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL ) |
| { |
| try |
| { |
| init(driver, dbURL, new Properties()); |
| return new XBooleanStatic(true); |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| } |
| |
| /** |
| * @param exprContext |
| * @param protocolElem |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, Element protocolElem ) |
| { |
| try |
| { |
| initFromElement(protocolElem); |
| return new XBooleanStatic(true); |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| } |
| |
| /** |
| * @param exprContext |
| * @param list |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, NodeList list ) |
| { |
| try |
| { |
| initFromElement( (Element) list.item(0) ); |
| return new XBooleanStatic(true); |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| } |
| |
| /** |
| * Create an XConnection object with user ID and password. |
| * @param exprContext |
| * @param driver JDBC driver of the form foo.bar.Driver. |
| * @param dbURL database URL of the form jdbc:subprotocol:subname. |
| * @param user user ID. |
| * @param password connection password. |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, String user, String password ) |
| { |
| try |
| { |
| Properties prop = new Properties(); |
| prop.put("user", user); |
| prop.put("password", password); |
| |
| init(driver, dbURL, prop); |
| |
| return new XBooleanStatic(true); |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| } |
| |
| |
| /** |
| * Create an XConnection object with a connection protocol |
| * @param exprContext |
| * @param driver JDBC driver of the form foo.bar.Driver. |
| * @param dbURL database URL of the form jdbc:subprotocol:subname. |
| * @param protocolElem list of string tag/value connection arguments, |
| * normally including at least "user" and "password". |
| * @return |
| */ |
| public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem ) |
| { |
| try |
| { |
| Properties prop = new Properties(); |
| |
| NamedNodeMap atts = protocolElem.getAttributes(); |
| |
| for (int i = 0; i < atts.getLength(); i++) |
| { |
| prop.put(atts.item(i).getNodeName(), atts.item(i).getNodeValue()); |
| } |
| |
| init(driver, dbURL, prop); |
| |
| return new XBooleanStatic(true); |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return new XBooleanStatic(false); |
| } |
| } |
| |
| |
| /** |
| * Allow the database connection information to be sepcified in |
| * the XML tree. The connection information could also be |
| * externally originated and passed in as an XSL Parameter. |
| * The required XML Format is as follows. |
| * A document fragment is needed to specify the connection information |
| * the top tag name is not specific for this code, we are only interested |
| * in the tags inside. |
| * <DBINFO-TAG> |
| * Specify the driver name for this connection pool |
| * <dbdriver>drivername</dbdriver> |
| * Specify the URL for the driver in this connection pool |
| * <dburl>url</dburl> |
| * Specify the password for this connection pool |
| * <password>password</password> |
| * Specify the username for this connection pool |
| * <user>username</user> |
| * You can add extra protocol items including the User Name & Password |
| * with the protocol tag. For each extra protocol item, add a new element |
| * where the name of the item is specified as the name attribute and |
| * and its value as the elements value. |
| * <protocol name="name of value">value</protocol> |
| * </DBINFO-TAG> |
| * @param e |
| * @return |
| * @throws SQLException |
| */ |
| private void initFromElement( Element e )throws SQLException |
| { |
| |
| Properties prop = new Properties(); |
| String driver = ""; |
| String dbURL = ""; |
| Node n = e.getFirstChild(); |
| |
| if (null == n) return; // really need to throw an error |
| |
| do |
| { |
| String nName = n.getNodeName(); |
| |
| if (nName.equalsIgnoreCase("dbdriver")) |
| { |
| driver = ""; |
| Node n1 = n.getFirstChild(); |
| if (null != n1) |
| { |
| driver = n1.getNodeValue(); |
| } |
| } |
| |
| if (nName.equalsIgnoreCase("dburl")) |
| { |
| dbURL = ""; |
| Node n1 = n.getFirstChild(); |
| if (null != n1) |
| { |
| dbURL = n1.getNodeValue(); |
| } |
| } |
| |
| if (nName.equalsIgnoreCase("password")) |
| { |
| String s = ""; |
| Node n1 = n.getFirstChild(); |
| if (null != n1) |
| { |
| s = n1.getNodeValue(); |
| } |
| prop.put("password", s); |
| } |
| |
| if (nName.equalsIgnoreCase("user")) |
| { |
| String s = ""; |
| Node n1 = n.getFirstChild(); |
| if (null != n1) |
| { |
| s = n1.getNodeValue(); |
| } |
| prop.put("user", s); |
| } |
| |
| if (nName.equalsIgnoreCase("protocol")) |
| { |
| String Name = ""; |
| |
| NamedNodeMap attrs = n.getAttributes(); |
| Node n1 = attrs.getNamedItem("name"); |
| if (null != n1) |
| { |
| String s = ""; |
| Name = n1.getNodeValue(); |
| |
| Node n2 = n.getFirstChild(); |
| if (null != n2) s = n2.getNodeValue(); |
| |
| prop.put(Name, s); |
| } |
| } |
| |
| } while ( (n = n.getNextSibling()) != null); |
| |
| init(driver, dbURL, prop); |
| } |
| |
| |
| |
| /** |
| * Initilize is being called because we did not have an |
| * existing Connection Pool, so let's see if we created one |
| * already or lets create one ourselves. |
| * @param driver |
| * @param dbURL |
| * @param prop |
| * @return |
| * @throws SQLException |
| */ |
| private void init( String driver, String dbURL, Properties prop )throws SQLException |
| { |
| Connection con = null; |
| |
| if (DEBUG) |
| System.out.println("XConnection, Connection Init"); |
| |
| String user = prop.getProperty("user"); |
| if (user == null) user = ""; |
| |
| String passwd = prop.getProperty("password"); |
| if (passwd == null) passwd = ""; |
| |
| |
| String poolName = driver + dbURL + user + passwd; |
| ConnectionPool cpool = m_PoolMgr.getPool(poolName); |
| |
| if (cpool == null) |
| { |
| |
| if (DEBUG) |
| { |
| System.out.println("XConnection, Creating Connection"); |
| System.out.println(" Driver :" + driver); |
| System.out.println(" URL :" + dbURL); |
| System.out.println(" user :" + user); |
| System.out.println(" passwd :" + passwd); |
| } |
| |
| |
| DefaultConnectionPool defpool = new DefaultConnectionPool(); |
| |
| if ((DEBUG) && (defpool == null)) |
| System.out.println("Failed to Create a Default Connection Pool"); |
| |
| defpool.setDriver(driver); |
| defpool.setURL(dbURL); |
| defpool.setProtocol(prop); |
| |
| // Only enable pooling in the default pool if we are explicatly |
| // told too. |
| if (m_DefaultPoolingEnabled) defpool.setPoolEnabled(true); |
| |
| m_PoolMgr.registerPool(poolName, defpool); |
| m_ConnectionPool = defpool; |
| } |
| else |
| { |
| m_ConnectionPool = cpool; |
| } |
| |
| m_IsDefaultPool = true; |
| |
| // |
| // Let's test to see if we really can connect |
| // Just remember to give it back after the test. |
| // |
| try |
| { |
| con = m_ConnectionPool.getConnection(); |
| } |
| catch(SQLException e) |
| { |
| if (con != null) |
| { |
| m_ConnectionPool.releaseConnectionOnError(con); |
| con = null; |
| } |
| throw e; |
| } |
| finally |
| { |
| m_ConnectionPool.releaseConnection(con); |
| } |
| } |
| |
| |
| /** |
| * Execute a query statement by instantiating an |
| * @param exprContext |
| * @param queryString the SQL query. |
| * @return XStatement implements NodeIterator. |
| * @throws SQLException |
| * @link org.apache.xalan.lib.sql.XStatement XStatement} |
| * object. The XStatement executes the query, and uses the result set |
| * to create a |
| * @link org.apache.xalan.lib.sql.RowSet RowSet}, |
| * a row-set element. |
| */ |
| public DTM query( ExpressionContext exprContext, String queryString ) |
| { |
| Connection con = null; |
| Statement stmt = null; |
| ResultSet rs = null; |
| |
| DTMManagerDefault mgrDefault = null; |
| SQLDocument doc = null; |
| |
| try |
| { |
| if (DEBUG) System.out.println("query()"); |
| if (null == m_ConnectionPool) |
| { |
| // Build an Error Document, NOT Connected |
| return null; |
| } |
| |
| try |
| { |
| con = m_ConnectionPool.getConnection(); |
| stmt = con.createStatement(); |
| rs = stmt.executeQuery(queryString); |
| } |
| catch(SQLException e) |
| { |
| // We have not created a document yet, so lets close the |
| // connection ourselves then let the process deal with the |
| // error. |
| // |
| try { if (null != rs) rs.close(); } |
| catch(Exception e1) {} |
| try { if (null != stmt) stmt.close(); } |
| catch(Exception e1) { } |
| try { |
| if (null != con) m_ConnectionPool.releaseConnectionOnError(con); |
| } catch(Exception e1) { } |
| |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| |
| if (DEBUG) System.out.println("..creatingSQLDocument"); |
| |
| DTMManager mgr = |
| ((XPathContext.XPathExpressionContext)exprContext).getDTMManager(); |
| mgrDefault = (DTMManagerDefault) mgr; |
| int dtmIdent = mgrDefault.getFirstFreeDTMID(); |
| |
| doc = |
| new SQLDocument( |
| mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS , |
| m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled); |
| |
| if (null != doc) |
| { |
| if (DEBUG) System.out.println("..returning Document"); |
| |
| // Register our document |
| mgrDefault.addDTM(doc, dtmIdent); |
| |
| // also keep a local reference |
| m_OpenSQLDocuments.addElement(doc); |
| return doc; |
| } |
| else |
| { |
| return null; |
| } |
| } |
| catch(SQLException e) |
| { |
| if ((doc != null) && (mgrDefault != null)) |
| { |
| doc.closeOnError(); |
| mgrDefault.release(doc, true); |
| } |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| catch (Exception e) |
| { |
| if ((doc != null) && (mgrDefault != null)) |
| { |
| doc.closeOnError(); |
| mgrDefault.release(doc, true); |
| } |
| |
| if (DEBUG) System.out.println("exception in query()"); |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| finally |
| { |
| if (DEBUG) System.out.println("leaving query()"); |
| } |
| } |
| |
| /** |
| * Execute a parameterized query statement by instantiating an |
| * @param exprContext |
| * @param queryString the SQL query. |
| * @return XStatement implements NodeIterator. |
| * @throws SQLException |
| * @link org.apache.xalan.lib.sql.XStatement XStatement} |
| * object. The XStatement executes the query, and uses the result set |
| * to create a |
| * @link org.apache.xalan.lib.sql.RowSet RowSet}, |
| * a row-set element. |
| */ |
| public DTM pquery( ExpressionContext exprContext, String queryString ) |
| { |
| Connection con = null; |
| PreparedStatement stmt = null; |
| ResultSet rs = null; |
| |
| try |
| { |
| int indx; |
| |
| try |
| { |
| con = m_ConnectionPool.getConnection(); |
| stmt = con.prepareStatement(queryString); |
| } |
| catch(SQLException e) |
| { |
| // We have not created a document yet, so lets close the |
| // connection ourselves then let the process deal with the |
| // error. |
| // |
| try { if (null != stmt) stmt.close(); } |
| catch(Exception e1) { } |
| try { |
| if (null != con) m_ConnectionPool.releaseConnectionOnError(con); |
| } catch(Exception e1) {} |
| |
| // Re throw the error so the process can handle the error |
| // normally |
| throw e; |
| } |
| |
| if (DEBUG) System.out.println("..building Prepared Statement"); |
| |
| try |
| { |
| Enumeration enum = m_ParameterList.elements(); |
| indx = 1; |
| while (enum.hasMoreElements()) |
| { |
| QueryParameter qp = (QueryParameter) enum.nextElement(); |
| setParameter(indx, stmt, qp); |
| indx++; |
| } |
| |
| rs = stmt.executeQuery(); |
| } |
| catch(SQLException e) |
| { |
| // We have not created a document yet, so lets close the |
| // connection ourselves then let the process deal with the |
| // error. |
| // |
| try { if (null != rs) rs.close(); } |
| catch(Exception e1) { } |
| try { if (null != stmt) stmt.close(); } |
| catch(Exception e1) { } |
| try { |
| if (null != con) m_ConnectionPool.releaseConnectionOnError(con); |
| } catch(Exception e1) { } |
| |
| // Re throw the error so the process can handle the error |
| // normally |
| throw e; |
| } |
| |
| if (DEBUG) System.out.println("..creatingSQLDocument"); |
| |
| DTMManager mgr = |
| ((XPathContext.XPathExpressionContext)exprContext).getDTMManager(); |
| DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr; |
| int dtmIdent = mgrDefault.getFirstFreeDTMID(); |
| |
| SQLDocument doc = |
| new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS, |
| m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled); |
| |
| if (null != doc) |
| { |
| if (DEBUG) System.out.println("..returning Document"); |
| |
| // Register our document |
| mgrDefault.addDTM(doc, dtmIdent); |
| |
| // also keep a local reference |
| m_OpenSQLDocuments.addElement(doc); |
| return doc; |
| } |
| else |
| { |
| // Build Error Doc, BAD Result Set |
| return null; |
| } |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Execute a parameterized query statement by instantiating an |
| * @param exprContext |
| * @param queryString the SQL query. |
| * @param typeInfo |
| * @return XStatement implements NodeIterator. |
| * @throws SQLException |
| * @link org.apache.xalan.lib.sql.XStatement XStatement} |
| * object. The XStatement executes the query, and uses the result set |
| * to create a |
| * @link org.apache.xalan.lib.sql.RowSet RowSet}, |
| * a row-set element. |
| * This method allows for the user to pass in a comma seperated |
| * String that represents a list of parameter types. If supplied |
| * the parameter types will be used to overload the current types |
| * in the current parameter list. |
| */ |
| public DTM pquery( ExpressionContext exprContext, String queryString, String typeInfo ) |
| { |
| Connection con = null; |
| PreparedStatement stmt = null; |
| ResultSet rs = null; |
| |
| try |
| { |
| int indx; |
| |
| // Parse up the parameter types that were defined |
| // with the query |
| StringTokenizer plist = new StringTokenizer(typeInfo); |
| |
| // Override the existing type that is stored in the |
| // parameter list. If there are more types than parameters |
| // ignore for now, a more meaningfull error should occur |
| // when the actual query is executed. |
| indx = 0; |
| while (plist.hasMoreTokens()) |
| { |
| String value = plist.nextToken(); |
| QueryParameter qp = (QueryParameter) m_ParameterList.elementAt(indx); |
| if ( null != qp ) |
| { |
| qp.setType(value); |
| } |
| |
| indx++; |
| } |
| |
| try |
| { |
| con = m_ConnectionPool.getConnection(); |
| stmt = con.prepareStatement(queryString); |
| } |
| catch(SQLException e) |
| { |
| // We have not created a document yet, so lets close the |
| // connection ourselves then let the process deal with the |
| // error. |
| // |
| try { if (null != stmt) stmt.close(); } |
| catch(Exception e1) { } |
| try { |
| if (null != con) m_ConnectionPool.releaseConnectionOnError(con); |
| } catch(Exception e1) { } |
| |
| // Re throw the error so the process can handle the error |
| // normally |
| throw e; |
| } |
| |
| |
| |
| if (DEBUG) System.out.println("..building Prepared Statement"); |
| |
| try |
| { |
| Enumeration enum = m_ParameterList.elements(); |
| indx = 1; |
| while (enum.hasMoreElements()) |
| { |
| QueryParameter qp = (QueryParameter) enum.nextElement(); |
| setParameter(indx, stmt, qp); |
| indx++; |
| } |
| |
| rs = stmt.executeQuery(); |
| } |
| catch(SQLException e) |
| { |
| // We have not created a document yet, so lets close the |
| // connection ourselves then let the process deal with the |
| // error. |
| // |
| try { if (null != rs) rs.close(); } |
| catch(Exception e1) { /* Empty */ } |
| try { if (null != stmt) stmt.close(); } |
| catch(Exception e1) { /* Empty */ } |
| try { |
| if (null != con) m_ConnectionPool.releaseConnectionOnError(con); |
| } catch(Exception e1) { /* Empty */ } |
| |
| // Re throw the error so the process can handle the error |
| // normally |
| throw e; |
| } |
| |
| |
| if (DEBUG) System.out.println("..creatingSQLDocument"); |
| |
| DTMManager mgr = |
| ((XPathContext.XPathExpressionContext)exprContext).getDTMManager(); |
| DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr; |
| int dtmIdent = mgrDefault.getFirstFreeDTMID(); |
| |
| SQLDocument doc = |
| new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS , |
| m_ConnectionPool, con, stmt, rs, m_IsStreamingEnabled); |
| |
| if (null != doc) |
| { |
| if (DEBUG) System.out.println("..returning Document"); |
| |
| // Register our document |
| mgrDefault.addDTM(doc, dtmIdent); |
| |
| // also keep a local reference |
| m_OpenSQLDocuments.addElement(doc); |
| return doc; |
| } |
| else |
| { |
| // Build Error Doc, BAD Result Set |
| return null; |
| } |
| } |
| catch(SQLException e) |
| { |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| catch (Exception e) |
| { |
| buildErrorDocument(exprContext, e); |
| return null; |
| } |
| } |
| |
| /** |
| * Add an untyped value to the parameter list. |
| * @param value |
| * @return |
| */ |
| public void addParameter( String value ) |
| { |
| addParameterWithType(value, null); |
| } |
| |
| /** |
| * Add a typed parameter to the parameter list. |
| * @param value |
| * @param Type |
| * @return |
| */ |
| public void addParameterWithType( String value, String Type ) |
| { |
| m_ParameterList.addElement( new QueryParameter(value, Type) ); |
| } |
| |
| |
| /** |
| * Add a single parameter to the parameter list |
| * formatted as an Element |
| * @param e |
| * @return |
| */ |
| public void addParameterFromElement( Element e ) |
| { |
| NamedNodeMap attrs = e.getAttributes(); |
| Node Type = attrs.getNamedItem("type"); |
| Node n1 = e.getFirstChild(); |
| if (null != n1) |
| { |
| String value = n1.getNodeValue(); |
| if (value == null) value = ""; |
| m_ParameterList.addElement( new QueryParameter(value, Type.getNodeValue()) ); |
| } |
| } |
| |
| |
| /** |
| * Add a section of parameters to the Parameter List |
| * Do each element from the list |
| * @param nl |
| * @return |
| */ |
| public void addParameterFromElement( NodeList nl ) |
| { |
| // |
| // Each child of the NodeList represents a node |
| // match from the select= statment. Process each |
| // of them as a seperate list. |
| // The XML Format is as follows |
| // |
| // <START-TAG> |
| // <TAG1 type="int">value</TAG1> |
| // <TAGA type="int">value</TAGA> |
| // <TAG2 type="string">value</TAG2> |
| // </START-TAG> |
| // |
| // The XSL to process this is formatted as follows |
| // <xsl:param name="plist" select="//START-TAG" /> |
| // <sql:addParameter( $plist ); |
| // |
| int count = nl.getLength(); |
| for (int x=0; x<count; x++) |
| { |
| addParameters( (Element) nl.item(x)); |
| } |
| } |
| |
| /** |
| * @param elem |
| * @return |
| */ |
| private void addParameters( Element elem ) |
| { |
| // |
| // Process all of the Child Elements |
| // The format is as follows |
| // |
| //<TAG type ="typeid">value</TAG> |
| //<TAG1 type ="typeid">value</TAG1> |
| //<TAGA type ="typeid">value</TAGA> |
| // |
| // The name of the Node is not important just is value |
| // and if it contains a type attribute |
| |
| Node n = elem.getFirstChild(); |
| |
| if (null == n) return; |
| |
| do |
| { |
| if (n.getNodeType() == Node.ELEMENT_NODE) |
| { |
| NamedNodeMap attrs = n.getAttributes(); |
| Node Type = attrs.getNamedItem("type"); |
| String TypeStr; |
| |
| if (Type == null) TypeStr = "string"; |
| else TypeStr = Type.getNodeValue(); |
| |
| Node n1 = n.getFirstChild(); |
| if (null != n1) |
| { |
| String value = n1.getNodeValue(); |
| if (value == null) value = ""; |
| |
| |
| m_ParameterList.addElement( |
| new QueryParameter(value, TypeStr) ); |
| } |
| } |
| } while ( (n = n.getNextSibling()) != null); |
| } |
| |
| /** |
| * @return |
| */ |
| public void clearParameters( ) |
| { |
| m_ParameterList.removeAllElements(); |
| } |
| |
| /** |
| * There is a problem with some JDBC drivers when a Connection |
| * is open and the JVM shutsdown. If there is a problem, there |
| * is no way to control the currently open connections in the |
| * pool. So for the default connection pool, the actuall pooling |
| * mechinsm is disabled by default. The Stylesheet designer can |
| * re-enabled pooling to take advantage of connection pools. |
| * The connection pool can even be disabled which will close all |
| * outstanding connections. |
| * @return |
| */ |
| public void enableDefaultConnectionPool( ) |
| { |
| |
| if (DEBUG) |
| System.out.println("Enabling Default Connection Pool"); |
| |
| m_DefaultPoolingEnabled = true; |
| |
| if (m_ConnectionPool == null) return; |
| if (m_IsDefaultPool) return; |
| |
| m_ConnectionPool.setPoolEnabled(true); |
| |
| } |
| |
| /** |
| * See enableDefaultConnectionPool |
| * @return |
| */ |
| public void disableDefaultConnectionPool( ) |
| { |
| if (DEBUG) |
| System.out.println("Disabling Default Connection Pool"); |
| |
| m_DefaultPoolingEnabled = false; |
| |
| if (m_ConnectionPool == null) return; |
| if (!m_IsDefaultPool) return; |
| |
| m_ConnectionPool.setPoolEnabled(false); |
| } |
| |
| |
| /** |
| * Control how the SQL Document uses memory. In Streaming Mode, |
| * memory consumption is greatly reduces so you can have queries |
| * of unlimited size but it will not let you traverse the data |
| * more than once. |
| * @return |
| */ |
| public void enableStreamingMode( ) |
| { |
| |
| if (DEBUG) |
| System.out.println("Enabling Streaming Mode"); |
| |
| m_IsStreamingEnabled = true; |
| } |
| |
| /** |
| * Control how the SQL Document uses memory. In Streaming Mode, |
| * memory consumption is greatly reduces so you can have queries |
| * of unlimited size but it will not let you traverse the data |
| * more than once. |
| * @return |
| */ |
| public void disableStreamingMode( ) |
| { |
| |
| if (DEBUG) |
| System.out.println("Disable Streaming Mode"); |
| |
| m_IsStreamingEnabled = false; |
| } |
| |
| /** |
| * Provide access to the last error that occued. This error |
| * may be over written when the next operation occurs. |
| * @return |
| */ |
| public DTM getError( ) |
| { |
| return m_Error; |
| } |
| |
| /** |
| * Close the connection to the data source. |
| * @return |
| * @throws SQLException |
| */ |
| public void close( )throws SQLException |
| { |
| |
| if (DEBUG) |
| System.out.println("Entering XConnection.close"); |
| |
| // |
| // This function is included just for Legacy support |
| // If it is really called then we must me using a single |
| // document interface, so close all open documents. |
| while(m_OpenSQLDocuments.size() != 0) |
| { |
| SQLDocument d = (SQLDocument) m_OpenSQLDocuments.elementAt(0); |
| d.close(); |
| m_OpenSQLDocuments.removeElementAt(0); |
| } |
| |
| if (DEBUG) |
| System.out.println("Exiting XConnection.close"); |
| |
| } |
| |
| /** |
| * Close the connection to the data source. Only close the connections |
| * for a single document. |
| * @param sqldoc |
| * @return |
| * @throws SQLException |
| */ |
| public void close( SQLDocument sqldoc )throws SQLException |
| { |
| if (DEBUG) |
| System.out.println("Entering XConnection.close"); |
| |
| int size = m_OpenSQLDocuments.size(); |
| |
| for(int x=0; x<size; x++) |
| { |
| SQLDocument d = (SQLDocument) m_OpenSQLDocuments.elementAt(x); |
| if (d == sqldoc) |
| { |
| d.close(); |
| m_OpenSQLDocuments.removeElementAt(x); |
| } |
| } |
| } |
| |
| /** |
| * Set the parameter for a Prepared Statement |
| * @param pos |
| * @param stmt |
| * @param p |
| * @return |
| * @throws SQLException |
| */ |
| public void setParameter( int pos, PreparedStatement stmt, QueryParameter p )throws SQLException |
| { |
| String type = p.getType(); |
| if (type.equalsIgnoreCase("string")) |
| { |
| stmt.setString(pos, p.getValue()); |
| } |
| |
| if (type.equalsIgnoreCase("bigdecimal")) |
| { |
| stmt.setBigDecimal(pos, new BigDecimal(p.getValue())); |
| } |
| |
| if (type.equalsIgnoreCase("boolean")) |
| { |
| Integer i = new Integer( p.getValue() ); |
| boolean b = ((i.intValue() != 0) ? false : true); |
| stmt.setBoolean(pos, b); |
| } |
| |
| if (type.equalsIgnoreCase("bytes")) |
| { |
| stmt.setBytes(pos, p.getValue().getBytes()); |
| } |
| |
| if (type.equalsIgnoreCase("date")) |
| { |
| stmt.setDate(pos, Date.valueOf(p.getValue())); |
| } |
| |
| if (type.equalsIgnoreCase("double")) |
| { |
| Double d = new Double(p.getValue()); |
| stmt.setDouble(pos, d.doubleValue() ); |
| } |
| |
| if (type.equalsIgnoreCase("float")) |
| { |
| Float f = new Float(p.getValue()); |
| stmt.setFloat(pos, f.floatValue()); |
| } |
| |
| if (type.equalsIgnoreCase("long")) |
| { |
| Long l = new Long(p.getValue()); |
| stmt.setLong(pos, l.longValue()); |
| } |
| |
| if (type.equalsIgnoreCase("short")) |
| { |
| Short s = new Short(p.getValue()); |
| stmt.setShort(pos, s.shortValue()); |
| } |
| |
| if (type.equalsIgnoreCase("time")) |
| { |
| stmt.setTime(pos, Time.valueOf(p.getValue()) ); |
| } |
| |
| if (type.equalsIgnoreCase("timestamp")) |
| { |
| |
| stmt.setTimestamp(pos, Timestamp.valueOf(p.getValue()) ); |
| } |
| |
| } |
| |
| /** |
| * @param exprContext |
| * @param excp |
| * @return |
| */ |
| private void buildErrorDocument( ExpressionContext exprContext, SQLException excp ) |
| { |
| try |
| { |
| DTMManager mgr = |
| ((XPathContext.XPathExpressionContext)exprContext).getDTMManager(); |
| DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr; |
| int dtmIdent = mgrDefault.getFirstFreeDTMID(); |
| |
| m_Error = new SQLErrorDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS, excp); |
| |
| // Register our document |
| mgrDefault.addDTM(m_Error, dtmIdent); |
| |
| } |
| catch(Exception e) |
| { |
| m_Error = null; |
| } |
| } |
| |
| /** |
| * @param exprContext |
| * @param excp |
| * @return |
| */ |
| private void buildErrorDocument( ExpressionContext exprContext, Exception excp ) |
| { |
| try |
| { |
| DTMManager mgr = |
| ((XPathContext.XPathExpressionContext)exprContext).getDTMManager(); |
| DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr; |
| int dtmIdent = mgrDefault.getFirstFreeDTMID(); |
| |
| m_Error = new SQLErrorDocument(mgr, dtmIdent<<DTMManager.IDENT_DTM_NODE_BITS, excp); |
| |
| // Register our document |
| mgrDefault.addDTM(m_Error, dtmIdent); |
| |
| } |
| catch(Exception e) |
| { |
| m_Error = null; |
| } |
| } |
| |
| /** |
| * @return |
| */ |
| protected void finalize( ) |
| { |
| if (DEBUG) System.out.println("In XConnection, finalize"); |
| try |
| { |
| close(); |
| } |
| catch(Exception e) |
| { |
| // Empty We are final Anyway |
| } |
| } |
| |
| } |