001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: XConnection.java 468638 2006-10-28 06:52:06Z minchau $ 020 */ 021 package org.apache.xalan.lib.sql; 022 023 import java.sql.Connection; 024 import java.sql.SQLException; 025 import java.sql.SQLWarning; 026 import java.util.Properties; 027 import java.util.StringTokenizer; 028 import java.util.Vector; 029 030 import javax.xml.transform.ErrorListener; 031 import javax.xml.transform.TransformerException; 032 033 import org.apache.xalan.extensions.ExpressionContext; 034 import org.apache.xml.dtm.DTM; 035 import org.apache.xml.dtm.DTMIterator; 036 import org.apache.xml.dtm.DTMManager; 037 import org.apache.xml.dtm.ref.DTMManagerDefault; 038 import org.apache.xml.dtm.ref.DTMNodeIterator; 039 import org.apache.xml.dtm.ref.DTMNodeProxy; 040 import org.apache.xpath.XPathContext; 041 import org.apache.xpath.objects.XBooleanStatic; 042 import org.apache.xpath.objects.XNodeSet; 043 import org.w3c.dom.Element; 044 import org.w3c.dom.NamedNodeMap; 045 import org.w3c.dom.Node; 046 import org.w3c.dom.NodeList; 047 048 /** 049 * An XSLT extension that allows a stylesheet to 050 * access JDBC data. 051 * 052 * It is accessed by specifying a namespace URI as follows: 053 * <pre> 054 * xmlns:sql="http://xml.apache.org/xalan/sql" 055 * </pre> 056 * 057 * From the stylesheet perspective, 058 * XConnection provides 3 extension functions: new(), 059 * query(), and close(). 060 * Use new() to call one of XConnection constructors, which 061 * establishes a JDBC driver connection to a data source and 062 * returns an XConnection object. 063 * Then use the XConnection object query() method to return a 064 * result set in the form of a row-set element. 065 * When you have finished working with the row-set, call the 066 * XConnection object close() method to terminate the connection. 067 */ 068 public class XConnection 069 { 070 071 /** 072 * Flag for DEBUG mode 073 */ 074 private static final boolean DEBUG = false; 075 076 /** 077 * The Current Connection Pool in Use. An XConnection can only 078 * represent one query at a time, prior to doing some type of query. 079 */ 080 private ConnectionPool m_ConnectionPool = null; 081 082 /** 083 * The DBMS Connection used to produce this SQL Document. 084 * Will be used to clear free up the database resources on 085 * close. 086 */ 087 private Connection m_Connection = null; 088 089 /** 090 * If a default Connection Pool is used. i.e. A connection Pool 091 * that is created internally, then do we actually allow pools 092 * to be created. Due to the archititure of the Xalan Extensions, 093 * there is no notification of when the Extension is being unloaded and 094 * as such, there is a good chance that JDBC COnnections are not closed. 095 * A finalized is provided to try and catch this situation but since 096 * support of finalizers is inconsistant across JVM's this may cause 097 * a problem. The robustness of the JDBC Driver is also at issue here. 098 * if a controlled shutdown is provided by the driver then default 099 * conntectiom pools are OK. 100 */ 101 private boolean m_DefaultPoolingEnabled = false; 102 103 104 /** 105 * As we do queries, we will produce SQL Documents. Any ony may produce 106 * one or more SQL Documents so that the current connection information 107 * may be easilly reused. This collection will hold a collection of all 108 * the documents created. As Documents are closed, they will be removed 109 * from the collection and told to free all the used resources. 110 */ 111 private Vector m_OpenSQLDocuments = new Vector(); 112 113 114 /** 115 * Let's keep a copy of the ConnectionPoolMgr in 116 * alive here so we are keeping the static pool alive 117 * We will also use this Pool Manager to register our default pools. 118 */ 119 private ConnectionPoolManager m_PoolMgr = new ConnectionPoolManager(); 120 121 /** 122 * For PreparedStatements, we need a place to 123 * to store the parameters in a vector. 124 */ 125 private Vector m_ParameterList = new Vector(); 126 127 /** 128 * Allow the SQL Extensions to return null on error. The Error information will 129 * be stored in a seperate Error Document that can easily be retrived using the 130 * getError() method. 131 * %REVIEW% This functionality will probably be buried inside the SQLDocument. 132 */ 133 private Exception m_Error = null; 134 135 /** 136 * When the Stylesheet wants to review the errors from a paticular 137 * document, it asks the XConnection. We need to track what document 138 * in the list of managed documents caused the last error. As SetError 139 * is called, it will record the document that had the error. 140 */ 141 private SQLDocument m_LastSQLDocumentWithError = null; 142 143 /** 144 * If true then full information should be returned about errors and warnings 145 * in getError(). This includes chained errors and warnings. 146 * If false (the default) then getError() returns just the first SQLException. 147 */ 148 private boolean m_FullErrors = false; 149 150 151 152 /** 153 * One a per XConnection basis there is a master QueryParser that is responsible 154 * for generating Query Parsers. This will allow us to cache previous instances 155 * so the inline parser execution time is minimized. 156 */ 157 private SQLQueryParser m_QueryParser = new SQLQueryParser(); 158 159 /** 160 */ 161 private boolean m_IsDefaultPool = false; 162 163 /** 164 * This flag will be used to indicate to the SQLDocument to use 165 * Streaming mode. Streeaming Mode will reduce the memory footprint 166 * to a fixed amount but will not let you traverse the tree more than 167 * once since the Row data will be reused for every Row in the Query. 168 */ 169 private boolean m_IsStreamingEnabled = true; 170 171 /** 172 * 173 */ 174 private boolean m_InlineVariables = false; 175 176 /** 177 * This flag will be used to indicate if multiple result sets are 178 * supported from the database. If they are, then the metadata element is 179 * moved to insude the row-set element and multiple row-set elements may 180 * be included under the sql root element. 181 */ 182 private boolean m_IsMultipleResultsEnabled = false; 183 184 /** 185 * This flag will be used to indicate if database preparedstatements 186 * should be cached. This also controls if the Java statement object 187 * should be cached. 188 */ 189 private boolean m_IsStatementCachingEnabled = false; 190 191 /** 192 */ 193 public XConnection( ) 194 { 195 } 196 197 /** 198 * Constructs a new XConnection and attempts to connect to a datasource as 199 * defined in the 200 * <code>connect(ExpressionContext exprContext, String connPoolName)</code> 201 * method. 202 * <code>org.apache.xalan.lib.sql.ConnectionPool</code> or a JNDI datasource. 203 * 204 * @param exprContext Context automatically passed from the XSLT sheet. 205 * @param name The name of the ConnectionPool or the JNDI DataSource path. 206 * 207 */ 208 public XConnection( ExpressionContext exprContext, String connPoolName ) 209 { 210 connect(exprContext, connPoolName); 211 } 212 213 /** 214 * @param exprContext 215 * @param driver 216 * @param dbURL 217 */ 218 public XConnection( ExpressionContext exprContext, String driver, String dbURL ) 219 { 220 connect(exprContext, driver, dbURL); 221 } 222 223 /** 224 * @param exprContext 225 * @param list 226 */ 227 public XConnection( ExpressionContext exprContext, NodeList list ) 228 { 229 connect(exprContext, list); 230 } 231 232 /** 233 * @param exprContext 234 * @param driver 235 * @param dbURL 236 * @param user 237 * @param password 238 */ 239 public XConnection( ExpressionContext exprContext, String driver, String dbURL, String user, String password ) 240 { 241 connect(exprContext, driver, dbURL, user, password); 242 } 243 244 /** 245 * @param exprContext 246 * @param driver 247 * @param dbURL 248 * @param protocolElem 249 */ 250 public XConnection( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem ) 251 { 252 connect(exprContext, driver, dbURL, protocolElem); 253 } 254 255 /** 256 * Returns an XConnection from either a user created 257 * <code>org.apache.xalan.lib.sql.ConnectionPool</code> or a JNDI datasource. 258 259 * 260 * This method first tries to resolve the passed name against 261 * <code>ConnectionPool</code>s registered with 262 * <code>ConnectionPoolManager</code>. 263 * If that fails, it attempts to find the name as a JNDI DataSource path. 264 * 265 * @param exprContext Context automatically passed from the XSLT sheet. 266 * @param name The name of the ConnectionPool or the JNDI DataSource path. 267 * 268 */ 269 public XBooleanStatic connect( ExpressionContext exprContext, String name ) 270 { 271 try 272 { 273 m_ConnectionPool = m_PoolMgr.getPool(name); 274 275 if (m_ConnectionPool == null) 276 { 277 //Try to create a jndi source with the passed name 278 ConnectionPool pool = new JNDIConnectionPool(name); 279 280 if (pool.testConnection()) 281 { 282 283 //JNDIConnectionPool seems good, so register it with the pool manager. 284 //Once registered, it will show up like other named ConnectionPool's, 285 //so the m_PoolMgr.getPool(name) method (above) will find it. 286 m_PoolMgr.registerPool(name, pool); 287 m_ConnectionPool = pool; 288 289 m_IsDefaultPool = false; 290 return new XBooleanStatic(true); 291 } 292 else 293 { 294 throw new IllegalArgumentException( 295 "Invalid ConnectionPool name or JNDI Datasource path: " + name); 296 } 297 } 298 else 299 { 300 m_IsDefaultPool = false; 301 return new XBooleanStatic(true); 302 } 303 } 304 catch (Exception e) 305 { 306 setError(e, exprContext); 307 return new XBooleanStatic(false); 308 } 309 310 } 311 312 313 /** 314 * Create an XConnection object with just a driver and database URL. 315 * @param exprContext 316 * @param driver JDBC driver of the form foo.bar.Driver. 317 * @param dbURL database URL of the form jdbc:subprotocol:subname. 318 * 319 */ 320 public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL ) 321 { 322 try 323 { 324 init(driver, dbURL, new Properties()); 325 return new XBooleanStatic(true); 326 } 327 catch(SQLException e) 328 { 329 setError(e,exprContext); 330 return new XBooleanStatic(false); 331 } 332 catch (Exception e) 333 { 334 setError(e,exprContext); 335 return new XBooleanStatic(false); 336 } 337 } 338 339 /** 340 * @param exprContext 341 * @param protocolElem 342 * 343 */ 344 public XBooleanStatic connect( ExpressionContext exprContext, Element protocolElem ) 345 { 346 try 347 { 348 initFromElement(protocolElem); 349 return new XBooleanStatic(true); 350 } 351 catch(SQLException e) 352 { 353 setError(e,exprContext); 354 return new XBooleanStatic(false); 355 } 356 catch (Exception e) 357 { 358 setError(e,exprContext); 359 return new XBooleanStatic(false); 360 } 361 } 362 363 /** 364 * @param exprContext 365 * @param list 366 * 367 */ 368 public XBooleanStatic connect( ExpressionContext exprContext, NodeList list ) 369 { 370 try 371 { 372 initFromElement( (Element) list.item(0) ); 373 return new XBooleanStatic(true); 374 } 375 catch(SQLException e) 376 { 377 setError(e, exprContext); 378 return new XBooleanStatic(false); 379 } 380 catch (Exception e) 381 { 382 setError(e,exprContext); 383 return new XBooleanStatic(false); 384 } 385 } 386 387 /** 388 * Create an XConnection object with user ID and password. 389 * @param exprContext 390 * @param driver JDBC driver of the form foo.bar.Driver. 391 * @param dbURL database URL of the form jdbc:subprotocol:subname. 392 * @param user user ID. 393 * @param password connection password. 394 * 395 */ 396 public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, String user, String password ) 397 { 398 try 399 { 400 Properties prop = new Properties(); 401 prop.put("user", user); 402 prop.put("password", password); 403 404 init(driver, dbURL, prop); 405 406 return new XBooleanStatic(true); 407 } 408 catch(SQLException e) 409 { 410 setError(e,exprContext); 411 return new XBooleanStatic(false); 412 } 413 catch (Exception e) 414 { 415 setError(e,exprContext); 416 return new XBooleanStatic(false); 417 } 418 } 419 420 421 /** 422 * Create an XConnection object with a connection protocol 423 * @param exprContext 424 * @param driver JDBC driver of the form foo.bar.Driver. 425 * @param dbURL database URL of the form jdbc:subprotocol:subname. 426 * @param protocolElem list of string tag/value connection arguments, 427 * normally including at least "user" and "password". 428 * 429 */ 430 public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL, Element protocolElem ) 431 { 432 try 433 { 434 Properties prop = new Properties(); 435 436 NamedNodeMap atts = protocolElem.getAttributes(); 437 438 for (int i = 0; i < atts.getLength(); i++) 439 { 440 prop.put(atts.item(i).getNodeName(), atts.item(i).getNodeValue()); 441 } 442 443 init(driver, dbURL, prop); 444 445 return new XBooleanStatic(true); 446 } 447 catch(SQLException e) 448 { 449 setError(e,exprContext); 450 return new XBooleanStatic(false); 451 } 452 catch (Exception e) 453 { 454 setError(e, exprContext); 455 return new XBooleanStatic(false); 456 } 457 } 458 459 460 /** 461 * Allow the database connection information to be sepcified in 462 * the XML tree. The connection information could also be 463 * externally originated and passed in as an XSL Parameter. 464 * The required XML Format is as follows. 465 * A document fragment is needed to specify the connection information 466 * the top tag name is not specific for this code, we are only interested 467 * in the tags inside. 468 * <DBINFO-TAG> 469 * Specify the driver name for this connection pool 470 * <dbdriver>drivername</dbdriver> 471 * Specify the URL for the driver in this connection pool 472 * <dburl>url</dburl> 473 * Specify the password for this connection pool 474 * <password>password</password> 475 * Specify the username for this connection pool 476 * <user>username</user> 477 * You can add extra protocol items including the User Name & Password 478 * with the protocol tag. For each extra protocol item, add a new element 479 * where the name of the item is specified as the name attribute and 480 * and its value as the elements value. 481 * <protocol name="name of value">value</protocol> 482 * </DBINFO-TAG> 483 * @param e 484 * 485 * @throws SQLException 486 */ 487 private void initFromElement( Element e )throws SQLException 488 { 489 490 Properties prop = new Properties(); 491 String driver = ""; 492 String dbURL = ""; 493 Node n = e.getFirstChild(); 494 495 if (null == n) return; // really need to throw an error 496 497 do 498 { 499 String nName = n.getNodeName(); 500 501 if (nName.equalsIgnoreCase("dbdriver")) 502 { 503 driver = ""; 504 Node n1 = n.getFirstChild(); 505 if (null != n1) 506 { 507 driver = n1.getNodeValue(); 508 } 509 } 510 511 if (nName.equalsIgnoreCase("dburl")) 512 { 513 dbURL = ""; 514 Node n1 = n.getFirstChild(); 515 if (null != n1) 516 { 517 dbURL = n1.getNodeValue(); 518 } 519 } 520 521 if (nName.equalsIgnoreCase("password")) 522 { 523 String s = ""; 524 Node n1 = n.getFirstChild(); 525 if (null != n1) 526 { 527 s = n1.getNodeValue(); 528 } 529 prop.put("password", s); 530 } 531 532 if (nName.equalsIgnoreCase("user")) 533 { 534 String s = ""; 535 Node n1 = n.getFirstChild(); 536 if (null != n1) 537 { 538 s = n1.getNodeValue(); 539 } 540 prop.put("user", s); 541 } 542 543 if (nName.equalsIgnoreCase("protocol")) 544 { 545 String Name = ""; 546 547 NamedNodeMap attrs = n.getAttributes(); 548 Node n1 = attrs.getNamedItem("name"); 549 if (null != n1) 550 { 551 String s = ""; 552 Name = n1.getNodeValue(); 553 554 Node n2 = n.getFirstChild(); 555 if (null != n2) s = n2.getNodeValue(); 556 557 prop.put(Name, s); 558 } 559 } 560 561 } while ( (n = n.getNextSibling()) != null); 562 563 init(driver, dbURL, prop); 564 } 565 566 567 568 /** 569 * Initilize is being called because we did not have an 570 * existing Connection Pool, so let's see if we created one 571 * already or lets create one ourselves. 572 * @param driver 573 * @param dbURL 574 * @param prop 575 * 576 * @throws SQLException 577 */ 578 private void init( String driver, String dbURL, Properties prop )throws SQLException 579 { 580 Connection con = null; 581 582 if (DEBUG) 583 System.out.println("XConnection, Connection Init"); 584 585 String user = prop.getProperty("user"); 586 if (user == null) user = ""; 587 588 String passwd = prop.getProperty("password"); 589 if (passwd == null) passwd = ""; 590 591 592 String poolName = driver + dbURL + user + passwd; 593 ConnectionPool cpool = m_PoolMgr.getPool(poolName); 594 595 if (cpool == null) 596 { 597 598 if (DEBUG) 599 { 600 System.out.println("XConnection, Creating Connection"); 601 System.out.println(" Driver :" + driver); 602 System.out.println(" URL :" + dbURL); 603 System.out.println(" user :" + user); 604 System.out.println(" passwd :" + passwd); 605 } 606 607 608 DefaultConnectionPool defpool = new DefaultConnectionPool(); 609 610 if ((DEBUG) && (defpool == null)) 611 System.out.println("Failed to Create a Default Connection Pool"); 612 613 defpool.setDriver(driver); 614 defpool.setURL(dbURL); 615 defpool.setProtocol(prop); 616 617 // Only enable pooling in the default pool if we are explicatly 618 // told too. 619 if (m_DefaultPoolingEnabled) defpool.setPoolEnabled(true); 620 621 m_PoolMgr.registerPool(poolName, defpool); 622 m_ConnectionPool = defpool; 623 } 624 else 625 { 626 m_ConnectionPool = cpool; 627 } 628 629 m_IsDefaultPool = true; 630 631 // 632 // Let's test to see if we really can connect 633 // Just remember to give it back after the test. 634 // 635 try 636 { 637 con = m_ConnectionPool.getConnection(); 638 } 639 catch(SQLException e) 640 { 641 if (con != null) 642 { 643 m_ConnectionPool.releaseConnectionOnError(con); 644 con = null; 645 } 646 throw e; 647 } 648 finally 649 { 650 if ( con != null ) m_ConnectionPool.releaseConnection(con); 651 } 652 } 653 654 /** 655 * Allow the SQL Document to retrive a connection to be used 656 * to build the SQL Statement. 657 */ 658 public ConnectionPool getConnectionPool() 659 { 660 return m_ConnectionPool; 661 } 662 663 664 /** 665 * Execute a query statement by instantiating an 666 * @param exprContext 667 * @param queryString the SQL query. 668 * @return XStatement implements NodeIterator. 669 * @throws SQLException 670 * @link org.apache.xalan.lib.sql.XStatement XStatement} 671 * object. The XStatement executes the query, and uses the result set 672 * to create a 673 * @link org.apache.xalan.lib.sql.RowSet RowSet}, 674 * a row-set element. 675 */ 676 public DTM query( ExpressionContext exprContext, String queryString ) 677 { 678 SQLDocument doc = null; 679 680 try 681 { 682 if (DEBUG) System.out.println("pquery()"); 683 684 // Build an Error Document, NOT Connected. 685 if ( null == m_ConnectionPool ) return null; 686 687 SQLQueryParser query = 688 m_QueryParser.parse 689 (this, queryString, SQLQueryParser.NO_INLINE_PARSER); 690 691 doc = SQLDocument.getNewDocument(exprContext); 692 doc.execute(this, query); 693 694 // also keep a local reference 695 m_OpenSQLDocuments.addElement(doc); 696 } 697 catch (Exception e) 698 { 699 // OK We had an error building the document, let try and grab the 700 // error information and clean up our connections. 701 702 if (DEBUG) System.out.println("exception in query()"); 703 704 if (doc != null) 705 { 706 if (doc.hasErrors()) 707 { 708 setError(e, doc, doc.checkWarnings()); 709 } 710 711 doc.close(m_IsDefaultPool); 712 doc = null; 713 } 714 } 715 finally 716 { 717 if (DEBUG) System.out.println("leaving query()"); 718 } 719 720 // Doc will be null if there was an error 721 return doc; 722 } 723 724 /** 725 * Execute a parameterized query statement by instantiating an 726 * @param exprContext 727 * @param queryString the SQL query. 728 * @return XStatement implements NodeIterator. 729 * @throws SQLException 730 * @link org.apache.xalan.lib.sql.XStatement XStatement} 731 * object. The XStatement executes the query, and uses the result set 732 * to create a 733 * @link org.apache.xalan.lib.sql.RowSet RowSet}, 734 * a row-set element. 735 */ 736 public DTM pquery( ExpressionContext exprContext, String queryString ) 737 { 738 return(pquery(exprContext, queryString, null)); 739 } 740 741 /** 742 * Execute a parameterized query statement by instantiating an 743 * @param exprContext 744 * @param queryString the SQL query. 745 * @param typeInfo 746 * @return XStatement implements NodeIterator. 747 * @throws SQLException 748 * @link org.apache.xalan.lib.sql.XStatement XStatement} 749 * object. The XStatement executes the query, and uses the result set 750 * to create a 751 * @link org.apache.xalan.lib.sql.RowSet RowSet}, 752 * a row-set element. 753 * This method allows for the user to pass in a comma seperated 754 * String that represents a list of parameter types. If supplied 755 * the parameter types will be used to overload the current types 756 * in the current parameter list. 757 */ 758 public DTM pquery( ExpressionContext exprContext, String queryString, String typeInfo) 759 { 760 SQLDocument doc = null; 761 762 try 763 { 764 if (DEBUG) System.out.println("pquery()"); 765 766 // Build an Error Document, NOT Connected. 767 if ( null == m_ConnectionPool ) return null; 768 769 SQLQueryParser query = 770 m_QueryParser.parse 771 (this, queryString, SQLQueryParser.NO_OVERRIDE); 772 773 // If we are not using the inline parser, then let add the data 774 // to the parser so it can populate the SQL Statement. 775 if ( !m_InlineVariables ) 776 { 777 addTypeToData(typeInfo); 778 query.setParameters(m_ParameterList); 779 } 780 781 doc = SQLDocument.getNewDocument(exprContext); 782 doc.execute(this, query); 783 784 // also keep a local reference 785 m_OpenSQLDocuments.addElement(doc); 786 } 787 catch (Exception e) 788 { 789 // OK We had an error building the document, let try and grab the 790 // error information and clean up our connections. 791 792 if (DEBUG) System.out.println("exception in query()"); 793 794 if (doc != null) 795 { 796 if (doc.hasErrors()) 797 { 798 setError(e, doc, doc.checkWarnings()); 799 } 800 801 // If we are using the Default Connection Pool, then 802 // force the connection pool to flush unused connections. 803 doc.close(m_IsDefaultPool); 804 doc = null; 805 } 806 } 807 finally 808 { 809 if (DEBUG) System.out.println("leaving query()"); 810 } 811 812 // Doc will be null if there was an error 813 return doc; 814 } 815 816 /** 817 * The purpose of this routine is to force the DB cursor to skip forward 818 * N records. You should call this function after [p]query to help with 819 * pagination. i.e. Perfrom your select, then skip forward past the records 820 * you read previously. 821 * 822 * @param exprContext 823 * @param o 824 * @param value 825 */ 826 public void skipRec( ExpressionContext exprContext, Object o, int value ) 827 { 828 SQLDocument sqldoc = null; 829 DTMNodeIterator nodei = null; 830 831 sqldoc = locateSQLDocument( exprContext, o); 832 if (sqldoc != null) sqldoc.skip(value); 833 } 834 835 836 837 private void addTypeToData(String typeInfo) 838 { 839 int indx; 840 841 if ( typeInfo != null && m_ParameterList != null ) 842 { 843 // Parse up the parameter types that were defined 844 // with the query 845 StringTokenizer plist = new StringTokenizer(typeInfo); 846 847 // Override the existing type that is stored in the 848 // parameter list. If there are more types than parameters 849 // ignore for now, a more meaningfull error should occur 850 // when the actual query is executed. 851 indx = 0; 852 while (plist.hasMoreTokens()) 853 { 854 String value = plist.nextToken(); 855 QueryParameter qp = (QueryParameter) m_ParameterList.elementAt(indx); 856 if ( null != qp ) 857 { 858 qp.setTypeName(value); 859 } 860 861 indx++; 862 } 863 } 864 } 865 866 /** 867 * Add an untyped value to the parameter list. 868 * @param value 869 * 870 */ 871 public void addParameter( String value ) 872 { 873 addParameterWithType(value, null); 874 } 875 876 /** 877 * Add a typed parameter to the parameter list. 878 * @param value 879 * @param Type 880 * 881 */ 882 public void addParameterWithType( String value, String Type ) 883 { 884 m_ParameterList.addElement( new QueryParameter(value, Type) ); 885 } 886 887 888 /** 889 * Add a single parameter to the parameter list 890 * formatted as an Element 891 * @param e 892 * 893 */ 894 public void addParameterFromElement( Element e ) 895 { 896 NamedNodeMap attrs = e.getAttributes(); 897 Node Type = attrs.getNamedItem("type"); 898 Node n1 = e.getFirstChild(); 899 if (null != n1) 900 { 901 String value = n1.getNodeValue(); 902 if (value == null) value = ""; 903 m_ParameterList.addElement( new QueryParameter(value, Type.getNodeValue()) ); 904 } 905 } 906 907 908 /** 909 * Add a section of parameters to the Parameter List 910 * Do each element from the list 911 * @param nl 912 * 913 */ 914 public void addParameterFromElement( NodeList nl ) 915 { 916 // 917 // Each child of the NodeList represents a node 918 // match from the select= statment. Process each 919 // of them as a seperate list. 920 // The XML Format is as follows 921 // 922 // <START-TAG> 923 // <TAG1 type="int">value</TAG1> 924 // <TAGA type="int">value</TAGA> 925 // <TAG2 type="string">value</TAG2> 926 // </START-TAG> 927 // 928 // The XSL to process this is formatted as follows 929 // <xsl:param name="plist" select="//START-TAG" /> 930 // <sql:addParameter( $plist ); 931 // 932 int count = nl.getLength(); 933 for (int x=0; x<count; x++) 934 { 935 addParameters( (Element) nl.item(x)); 936 } 937 } 938 939 /** 940 * @param elem 941 * 942 */ 943 private void addParameters( Element elem ) 944 { 945 // 946 // Process all of the Child Elements 947 // The format is as follows 948 // 949 //<TAG type ="typeid">value</TAG> 950 //<TAG1 type ="typeid">value</TAG1> 951 //<TAGA type ="typeid">value</TAGA> 952 // 953 // The name of the Node is not important just is value 954 // and if it contains a type attribute 955 956 Node n = elem.getFirstChild(); 957 958 if (null == n) return; 959 960 do 961 { 962 if (n.getNodeType() == Node.ELEMENT_NODE) 963 { 964 NamedNodeMap attrs = n.getAttributes(); 965 Node Type = attrs.getNamedItem("type"); 966 String TypeStr; 967 968 if (Type == null) TypeStr = "string"; 969 else TypeStr = Type.getNodeValue(); 970 971 Node n1 = n.getFirstChild(); 972 if (null != n1) 973 { 974 String value = n1.getNodeValue(); 975 if (value == null) value = ""; 976 977 978 m_ParameterList.addElement( 979 new QueryParameter(value, TypeStr) ); 980 } 981 } 982 } while ( (n = n.getNextSibling()) != null); 983 } 984 985 /** 986 * 987 */ 988 public void clearParameters( ) 989 { 990 m_ParameterList.removeAllElements(); 991 } 992 993 /** 994 * There is a problem with some JDBC drivers when a Connection 995 * is open and the JVM shutsdown. If there is a problem, there 996 * is no way to control the currently open connections in the 997 * pool. So for the default connection pool, the actuall pooling 998 * mechinsm is disabled by default. The Stylesheet designer can 999 * re-enabled pooling to take advantage of connection pools. 1000 * The connection pool can even be disabled which will close all 1001 * outstanding connections. 1002 * 1003 * @deprecated Use setFeature("default-pool-enabled", "true"); 1004 */ 1005 public void enableDefaultConnectionPool( ) 1006 { 1007 1008 if (DEBUG) 1009 System.out.println("Enabling Default Connection Pool"); 1010 1011 m_DefaultPoolingEnabled = true; 1012 1013 if (m_ConnectionPool == null) return; 1014 if (m_IsDefaultPool) return; 1015 1016 m_ConnectionPool.setPoolEnabled(true); 1017 1018 } 1019 1020 /** 1021 * See enableDefaultConnectionPool 1022 * 1023 * @deprecated Use setFeature("default-pool-enabled", "false"); 1024 */ 1025 public void disableDefaultConnectionPool( ) 1026 { 1027 if (DEBUG) 1028 System.out.println("Disabling Default Connection Pool"); 1029 1030 m_DefaultPoolingEnabled = false; 1031 1032 if (m_ConnectionPool == null) return; 1033 if (!m_IsDefaultPool) return; 1034 1035 m_ConnectionPool.setPoolEnabled(false); 1036 } 1037 1038 1039 /** 1040 * Control how the SQL Document uses memory. In Streaming Mode, 1041 * memory consumption is greatly reduces so you can have queries 1042 * of unlimited size but it will not let you traverse the data 1043 * more than once. 1044 * 1045 * @deprecated Use setFeature("streaming", "true"); 1046 */ 1047 public void enableStreamingMode( ) 1048 { 1049 1050 if (DEBUG) 1051 System.out.println("Enabling Streaming Mode"); 1052 1053 m_IsStreamingEnabled = true; 1054 } 1055 1056 /** 1057 * Control how the SQL Document uses memory. In Streaming Mode, 1058 * memory consumption is greatly reduces so you can have queries 1059 * of unlimited size but it will not let you traverse the data 1060 * more than once. 1061 * 1062 * @deprecated Use setFeature("streaming", "false"); 1063 */ 1064 public void disableStreamingMode( ) 1065 { 1066 1067 if (DEBUG) 1068 System.out.println("Disable Streaming Mode"); 1069 1070 m_IsStreamingEnabled = false; 1071 } 1072 1073 /** 1074 * Provide access to the last error that occued. This error 1075 * may be over written when the next operation occurs. 1076 * 1077 */ 1078 public DTM getError( ) 1079 { 1080 if ( m_FullErrors ) 1081 { 1082 for ( int idx = 0 ; idx < m_OpenSQLDocuments.size() ; idx++ ) 1083 { 1084 SQLDocument doc = (SQLDocument)m_OpenSQLDocuments.elementAt(idx); 1085 SQLWarning warn = doc.checkWarnings(); 1086 if ( warn != null ) setError(null, doc, warn); 1087 } 1088 } 1089 1090 return(buildErrorDocument()); 1091 } 1092 1093 /** 1094 * Close the connection to the data source. 1095 * 1096 * @throws SQLException 1097 */ 1098 public void close( )throws SQLException 1099 { 1100 if (DEBUG) 1101 System.out.println("Entering XConnection.close()"); 1102 1103 // 1104 // This function is included just for Legacy support 1105 // If it is really called then we must me using a single 1106 // document interface, so close all open documents. 1107 1108 while(m_OpenSQLDocuments.size() != 0) 1109 { 1110 SQLDocument d = (SQLDocument) m_OpenSQLDocuments.elementAt(0); 1111 try 1112 { 1113 // If we are using the Default Connection Pool, then 1114 // force the connection pool to flush unused connections. 1115 d.close(m_IsDefaultPool); 1116 } 1117 catch (Exception se ) {} 1118 1119 m_OpenSQLDocuments.removeElementAt(0); 1120 } 1121 1122 if ( null != m_Connection ) 1123 { 1124 m_ConnectionPool.releaseConnection(m_Connection); 1125 m_Connection = null; 1126 } 1127 1128 if (DEBUG) 1129 System.out.println("Exiting XConnection.close"); 1130 } 1131 1132 /** 1133 * Close the connection to the data source. Only close the connections 1134 * for a single document. 1135 * 1136 * @throws SQLException 1137 */ 1138 1139 public void close(ExpressionContext exprContext, Object doc) throws SQLException 1140 { 1141 if (DEBUG) 1142 System.out.println("Entering XConnection.close(" + doc + ")"); 1143 1144 SQLDocument sqlDoc = locateSQLDocument(exprContext, doc); 1145 if (sqlDoc != null) 1146 { 1147 // If we are using the Default Connection Pool, then 1148 // force the connection pool to flush unused connections. 1149 sqlDoc.close(m_IsDefaultPool); 1150 m_OpenSQLDocuments.remove(sqlDoc); 1151 } 1152 } 1153 1154 1155 /** 1156 * When an SQL Document is returned as a DTM object, the XSL variable is actually 1157 * assigned as a DTMIterator. This is a helper function that will allow you to get 1158 * a reference to the original SQLDocument from the iterator. 1159 * 1160 * Original code submitted by 1161 * Moraine Didier mailto://didier.moraine@winterthur.be 1162 * @param doc 1163 * @return 1164 */ 1165 private SQLDocument locateSQLDocument(ExpressionContext exprContext, Object doc) 1166 { 1167 try 1168 { 1169 if (doc instanceof DTMNodeIterator) 1170 { 1171 DTMNodeIterator dtmIter = (DTMNodeIterator)doc; 1172 try 1173 { 1174 DTMNodeProxy root = (DTMNodeProxy)dtmIter.getRoot(); 1175 return (SQLDocument) root.getDTM(); 1176 } 1177 catch (Exception e) 1178 { 1179 XNodeSet xNS = (XNodeSet)dtmIter.getDTMIterator(); 1180 DTMIterator iter = (DTMIterator)xNS.getContainedIter(); 1181 DTM dtm = iter.getDTM(xNS.nextNode()); 1182 return (SQLDocument)dtm; 1183 } 1184 } 1185 1186 /* 1187 XNodeSet xNS = (XNodeSet)dtmIter.getDTMIterator(); 1188 OneStepIterator iter = (OneStepIterator)xNS.getContainedIter(); 1189 DTMManager aDTMManager = (DTMManager)iter.getDTMManager(); 1190 return (SQLDocument)aDTMManager.getDTM(xNS.nextNode()); 1191 */ 1192 setError(new Exception("SQL Extension:close - Can Not Identify SQLDocument"), exprContext); 1193 return null; 1194 } 1195 catch(Exception e) 1196 { 1197 setError(e, exprContext); 1198 return null; 1199 } 1200 } 1201 1202 /** 1203 * @param exprContext 1204 * @param excp 1205 * 1206 */ 1207 private SQLErrorDocument buildErrorDocument() 1208 { 1209 SQLErrorDocument eDoc = null; 1210 1211 if ( m_LastSQLDocumentWithError != null) 1212 { 1213 // @todo 1214 // Do we need to do something with this ?? 1215 // m_Error != null || (m_FullErrors && m_Warning != null) ) 1216 1217 ExpressionContext ctx = m_LastSQLDocumentWithError.getExpressionContext(); 1218 SQLWarning warn = m_LastSQLDocumentWithError.checkWarnings(); 1219 1220 1221 try 1222 { 1223 DTMManager mgr = 1224 ((XPathContext.XPathExpressionContext)ctx).getDTMManager(); 1225 DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr; 1226 int dtmIdent = mgrDefault.getFirstFreeDTMID(); 1227 1228 eDoc = new SQLErrorDocument( 1229 mgr, dtmIdent<<DTMManager.IDENT_DTM_NODE_BITS, 1230 m_Error, warn, m_FullErrors); 1231 1232 // Register our document 1233 mgrDefault.addDTM(eDoc, dtmIdent); 1234 1235 // Clear the error and warning. 1236 m_Error = null; 1237 m_LastSQLDocumentWithError = null; 1238 } 1239 catch(Exception e) 1240 { 1241 eDoc = null; 1242 } 1243 } 1244 1245 return(eDoc); 1246 } 1247 1248 1249 /** 1250 * This is an internal version of Set Error that is called withen 1251 * XConnection where there is no SQLDocument created yet. As in the 1252 * Connect statement or creation of the ConnectionPool. 1253 */ 1254 public void setError(Exception excp,ExpressionContext expr) 1255 { 1256 try 1257 { 1258 ErrorListener listen = expr.getErrorListener(); 1259 if ( listen != null && excp != null ) 1260 { 1261 1262 listen.warning( 1263 new TransformerException(excp.toString(), 1264 expr.getXPathContext().getSAXLocator(), excp)); 1265 } 1266 } 1267 catch(Exception e) {} 1268 } 1269 1270 /** 1271 * Set an error and/or warning on this connection. 1272 * 1273 */ 1274 public void setError(Exception excp, SQLDocument doc, SQLWarning warn) 1275 { 1276 1277 ExpressionContext cont = doc.getExpressionContext(); 1278 m_LastSQLDocumentWithError = doc; 1279 1280 try 1281 { 1282 ErrorListener listen = cont.getErrorListener(); 1283 if ( listen != null && excp != null ) 1284 listen.warning( 1285 new TransformerException(excp.toString(), 1286 cont.getXPathContext().getSAXLocator(), excp)); 1287 1288 if ( listen != null && warn != null ) 1289 { 1290 listen.warning(new TransformerException( 1291 warn.toString(), cont.getXPathContext().getSAXLocator(), warn)); 1292 } 1293 1294 // Assume there will be just one error, but perhaps multiple warnings. 1295 if ( excp != null ) m_Error = excp; 1296 1297 if ( warn != null ) 1298 { 1299 // Because the log may not have processed the previous warning yet 1300 // we need to make a new one. 1301 SQLWarning tw = 1302 new SQLWarning(warn.getMessage(), warn.getSQLState(), 1303 warn.getErrorCode()); 1304 SQLWarning nw = warn.getNextWarning(); 1305 while ( nw != null ) 1306 { 1307 tw.setNextWarning(new SQLWarning(nw.getMessage(), 1308 nw.getSQLState(), nw.getErrorCode())); 1309 1310 nw = nw.getNextWarning(); 1311 } 1312 1313 tw.setNextWarning( 1314 new SQLWarning(warn.getMessage(), warn.getSQLState(), 1315 warn.getErrorCode())); 1316 1317 // m_Warning = tw; 1318 1319 } 1320 } 1321 catch(Exception e) 1322 { 1323 //m_Error = null; 1324 } 1325 } 1326 1327 /** 1328 * Set feature options for this XConnection. 1329 * @param feature The name of the feature being set, currently supports (streaming, inline-variables, multiple-results, cache-statements, default-pool-enabled). 1330 * @param setting The new setting for the specified feature, currently "true" is true and anything else is false. 1331 * 1332 */ 1333 public void setFeature(String feature, String setting) 1334 { 1335 boolean value = false; 1336 1337 if ( "true".equalsIgnoreCase(setting) ) value = true; 1338 1339 if ( "streaming".equalsIgnoreCase(feature) ) 1340 { 1341 m_IsStreamingEnabled = value; 1342 } 1343 else if ( "inline-variables".equalsIgnoreCase(feature) ) 1344 { 1345 m_InlineVariables = value; 1346 } 1347 else if ( "multiple-results".equalsIgnoreCase(feature) ) 1348 { 1349 m_IsMultipleResultsEnabled = value; 1350 } 1351 else if ( "cache-statements".equalsIgnoreCase(feature) ) 1352 { 1353 m_IsStatementCachingEnabled = value; 1354 } 1355 else if ( "default-pool-enabled".equalsIgnoreCase(feature) ) 1356 { 1357 m_DefaultPoolingEnabled = value; 1358 1359 if (m_ConnectionPool == null) return; 1360 if (m_IsDefaultPool) return; 1361 1362 m_ConnectionPool.setPoolEnabled(value); 1363 } 1364 else if ( "full-errors".equalsIgnoreCase(feature) ) 1365 { 1366 m_FullErrors = value; 1367 } 1368 } 1369 1370 /** 1371 * Get feature options for this XConnection. 1372 * @param feature The name of the feature to get the setting for. 1373 * @return The setting of the specified feature. Will be "true" or "false" (null if the feature is not known) 1374 */ 1375 public String getFeature(String feature) 1376 { 1377 String value = null; 1378 1379 if ( "streaming".equalsIgnoreCase(feature) ) 1380 value = m_IsStreamingEnabled ? "true" : "false"; 1381 else if ( "inline-variables".equalsIgnoreCase(feature) ) 1382 value = m_InlineVariables ? "true" : "false"; 1383 else if ( "multiple-results".equalsIgnoreCase(feature) ) 1384 value = m_IsMultipleResultsEnabled ? "true" : "false"; 1385 else if ( "cache-statements".equalsIgnoreCase(feature) ) 1386 value = m_IsStatementCachingEnabled ? "true" : "false"; 1387 else if ( "default-pool-enabled".equalsIgnoreCase(feature) ) 1388 value = m_DefaultPoolingEnabled ? "true" : "false"; 1389 else if ( "full-errors".equalsIgnoreCase(feature) ) 1390 value = m_FullErrors ? "true" : "false"; 1391 1392 return(value); 1393 } 1394 1395 1396 1397 /** 1398 * 1399 */ 1400 protected void finalize( ) 1401 { 1402 if (DEBUG) System.out.println("In XConnection, finalize"); 1403 try 1404 { 1405 close(); 1406 } 1407 catch(Exception e) 1408 { 1409 // Empty We are final Anyway 1410 } 1411 } 1412 1413 }