dleslie 00/03/31 10:26:51
Added: src/org/apache/xalan/xslt/extensions RowSetLocator.java Log: Extension for performing a SQL query against a JDBC data source and constructing a <rowset> with the query result set. Last appeared in 0.18.5. Updated per underlying API changes. Uses DriverManager.getConnection(String url, Properties info) to connect to data source. A <protocol> extension element supples the info param. Call the connect extension function to perform the operation. Revision Changes Path 1.1 xml-xalan/src/org/apache/xalan/xslt/extensions/RowSetLocator.java Index: RowSetLocator.java =================================================================== /* * 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 [EMAIL PROTECTED] * * 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.xslt.extensions; import org.apache.xalan.xpath.*; import org.apache.xalan.xpath.xml.*; import org.w3c.dom.*; import org.apache.xerces.dom.*; import java.util.*; import java.sql.*; /** * RowSetLocator uses JDBC to connect to a database, execute a query, and return * a result set, then returns a lazy row-set DOM. This class can ONLY be used with * the Xerces liaison (org.apache.xalan.xpath.xdom.XercesLiaison) and Xerces XML parser, * since it extends the Xerces DOM implementation to create a lazy row-set DOM. */ public class RowSetLocator extends SimpleNodeLocator { /** * Create a SimpleNodeLocator object. */ public RowSetLocator(String driver, String dbURL) { super(); init(driver, dbURL); } /** * A JDBC driver of the form "foo.bar.Driver". */ public String m_driver; /** * A database URL of the form jdbc:subprotocol:subname. */ public String m_dbURL; /** * Connection properties */ static java.util.Properties m_protocol = new Properties(); /** * User ID */ public String m_userID = ""; /** * Password */ public String m_password = ""; /** * A list of arbitrary string tag/value pairs as connection * arguments; normally at least a "user" and "password" * property should be included. */ public static void protocol(org.apache.xalan.xslt.XSLProcessorContext context, org.apache.xalan.xslt.ElemExtensionCall protocolElem) { org.xml.sax.AttributeList atts = protocolElem.getAttributeList(); // added dml. int ln = atts.getLength(); for (int i = 0; i < ln; i++) { m_protocol.put(atts.getName(i), atts.getValue(i)); } } /** * The JDBC connection. */ public Connection m_connection = null; /** * The SQL statement, which lets us execute commands against the connection. */ Statement m_statement = null; /** * Initialize. */ private void init(String driver, String dbURL) { m_driver = driver; m_dbURL = dbURL; // System.out.println("init completed"); } /** * Execute the proprietary connect() function, which returns an * instance of XLocator. When the XPath object sees a return type * of XLocator, it calls the locationPath function that passes * in the connectArgs. The opPos and args params are not used * by this function. This really is just a factory function * for the XLocator instance, but this fact is hidden from the * user. * @param driver JDBC driver. * @param dbURL database URL of the form jdbc:subprotocol:subname. * @param sqlQuery typically a static SQL SELECT statement, which * is normally executed by the connectToNodes function when called by the XPath object. * @returns An XLocator object. */ // coming public static XLocator connect (String driver, String dbURL, String sqlQuery) rest is in m_protocol public static XLocator connect (String driver, String dbURL, String sqlQuery) { RowSetLocator locator = null; try { locator = new RowSetLocator(driver, dbURL); // The driver is installed by loading its class. Class.forName(locator.m_driver); // System.out.println("about to connect"); locator.m_connection = DriverManager.getConnection(locator.m_dbURL, m_protocol); // System.out.println("connection OK"); /* We could also turn autocommit off by putting ;autocommit=false on the URL. */ locator.m_connection.setAutoCommit(false); // Creating a statement lets us issue commands against // the connection. locator.m_statement = locator.m_connection.createStatement(); // System.out.println("ready to return XLocator"); } catch (Throwable e) { e.printStackTrace(); } return locator; } /** * Execute a connection and process the LocationPath, * The arguments to the static connect function * are re-passed to this function. * @param xpath The xpath that is executing. * @param context The current source tree context node. * @param opPos The current position in the xpath.m_opMap array. * @param connectArgs The same arguments that were passed to the * static connect function. * @returns the result of the query in an XNodeSet object. */ public XNodeSet connectToNodes(XPath xpath, XPathSupport execContext, Node context, int opPos, Vector connectArgs) { // System.out.println("connectToNodes called"); XNodeSet results = new XNodeSet(); //create empty XNodeSet; was new XNodeSet(xpath.m_callbacks) MutableNodeList mnl = results.mutableNodeset(); // RowSetLocator locator = null; try { // Select the rows. String query = ((XObject)connectArgs.elementAt(2)).str(); ResultSet rowSet = m_statement.executeQuery(query); // System.out.println("query executed"); DocumentImpl doc = new DocumentImpl(); Element elem = new RowSetElem(doc, "row-set", rowSet, this); doc.appendChild(elem); if((xpath.OP_LOCATIONPATH == xpath.getOpMap()[opPos])) { XNodeSet xnl = xpath.locationPath(execContext, doc, opPos, null, null, false); /* locationPath args were (doc, opPos); now takes 6 args (XPathSupport execContext, Node context, int opPos, NodeCallback callback, Object callbackInfo, boolean stopAtFirst) */ if(null != xnl) { mnl.addNodes(xnl.nodeset()); execContext.associateXLocatorToNode(doc, this); // was XPath method } } else { mnl.addNode(elem); execContext.associateXLocatorToNode(doc, this); // was XPath method } } catch (Throwable e) { e.printStackTrace(); } return results; } /** * The RowSetElem extends the behavior of ElementImpl * by lazily evaluating it's row children. * TODO: We'll need some sort of garbage collection for * large rowsets, so we can discard rows at the beginning * of the child list, or whatever. */ class RowSetElem extends ElementImpl { /** * The query result set. */ ResultSet m_rowSet; /** * The owning RowSetLocator. */ RowSetLocator m_locator; public RowSetElem(DocumentImpl ownerDoc, String name, ResultSet rowSet, RowSetLocator locator) { super(ownerDoc, name); m_rowSet = rowSet; m_locator = locator; try { ResultSetMetaData metadata = m_rowSet.getMetaData(); if(null != metadata) { int nColumns = metadata.getColumnCount(); setAttribute("n-columns", Integer.toString( nColumns )); for(int i = 1; i <= nColumns; i++) { Element colHeader = ownerDoc.createElement("col-header"); appendChild(colHeader); { String tableName = metadata.getTableName(i); if(tableName.length() > 0) colHeader.setAttribute("table-name", tableName); } { String catalog = metadata.getCatalogName(i); if(catalog.length() > 0) colHeader.setAttribute("catalog", catalog); } { String columnNname = metadata.getColumnName(i); if(null != columnNname) { if(columnNname.length() > 0) colHeader.setAttribute("column-name", columnNname); } } { String label = metadata.getColumnLabel(i); if(null != label) { if(label.length() > 0) colHeader.setAttribute("column-label", label); } } { int displaySize = metadata.getColumnDisplaySize(i); colHeader.setAttribute("display-size", Integer.toString(displaySize)); } { int precision = metadata.getPrecision(i); colHeader.setAttribute("precision", Integer.toString(precision)); } { String type = metadata.getColumnTypeName(i); if(type.length() > 0) colHeader.setAttribute("type", type); } { String schema = metadata.getSchemaName(i); if(schema.length() > 0) colHeader.setAttribute("schema", schema); } } } else { System.out.println("Could not get metadata!!"); } } catch (Throwable e) { e.printStackTrace(); } } /** * Test whether this node has any children. Convenience shorthand * for (Node.getFirstChild()!=null) */ public boolean hasChildNodes() { return getFirstChild() != null; } /** The first child of this Node, or null if none. */ public Node getFirstChild() { while ((m_count <= 1) && createRow()) { ; } return super.getFirstChild(); } // getFirstChild():Node /** The last child of this Node, or null if none. */ public Node getLastChild() { getLength(); return super.getLastChild(); } // getLastChild():Node int m_count = 0; /** * NodeList method: Count the immediate children of this node. * (Expensive, so try to avoid using). * @return int */ public int getLength() { // System.out.println("getLength called: "+m_count); while (createRow()) // Creates a RowElem and increments m_count. { ; } // System.out.println("counted: "+m_count); return m_count; } // getLength():int /** * Create a row child. For now, just create all the columns, * don't try to lazily evaluate. */ boolean createRow() { boolean didCreate = false; try { didCreate = m_rowSet.next(); if(didCreate) { ResultSetMetaData metadata = m_rowSet.getMetaData(); Document doc = this.getOwnerDocument(); Element row = new RowElem( (DocumentImpl)doc, "row"); this.appendChild(row); int nColumns = metadata.getColumnCount(); for(int i = 1; i <= nColumns; i++) { Element col = doc.createElement("col"); row.appendChild(col); String data = m_rowSet.getString(i); Text text = doc.createTextNode(data); col.appendChild(text); } m_count++; } } catch (Throwable e) { e.printStackTrace(); } return didCreate; } /** * NodeList method: Return the Nth immediate child of this node, or * null if the index is out of bounds. * @return org.w3c.dom.Node * @param Index int */ public Node item(int index) { while ((m_count <= index) && createRow()) { ; } return super.item(index); } // item(int):Node } /** * Set up a derived class for rows to override getNextSibling. */ class RowElem extends ElementImpl { public RowElem(DocumentImpl ownerDoc, String name) { super(ownerDoc, name); } /** The next child of this node's parent, or null if none */ public Node getNextSibling() { Node next = super.getNextSibling(); if(null == next) // Row has not yet been created. { RowSetElem rsElem = (RowSetElem)getParentNode(); rsElem.createRow(); next = super.getNextSibling(); } return next; } } }
