blob: 0de079892c70506519eeb6879b97245d8f9a79db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
package org.apache.xalan.lib.sql;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.TransformerException;
import org.apache.xalan.extensions.ExpressionContext;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.DTMManager;
import org.apache.xml.dtm.ref.DTMManagerDefault;
import org.apache.xml.dtm.ref.DTMNodeIterator;
import org.apache.xml.dtm.ref.DTMNodeProxy;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XBooleanStatic;
import org.apache.xpath.objects.XNodeSet;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* An XSLT extension that allows a stylesheet to
* access JDBC data.
*
* It is accessed by specifying a namespace URI as follows:
* <pre>
* xmlns:sql="http://xml.apache.org/xalan/sql"
* </pre>
*
* 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;
/**
* The DBMS Connection used to produce this SQL Document.
* Will be used to clear free up the database resources on
* close.
*/
private Connection m_Connection = 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 Exception m_Error = null;
/**
* When the Stylesheet wants to review the errors from a paticular
* document, it asks the XConnection. We need to track what document
* in the list of managed documents caused the last error. As SetError
* is called, it will record the document that had the error.
*/
private SQLDocument m_LastSQLDocumentWithError = null;
/**
* If true then full information should be returned about errors and warnings
* in getError(). This includes chained errors and warnings.
* If false (the default) then getError() returns just the first SQLException.
*/
private boolean m_FullErrors = false;
/**
* One a per XConnection basis there is a master QueryParser that is responsible
* for generating Query Parsers. This will allow us to cache previous instances
* so the inline parser execution time is minimized.
*/
private SQLQueryParser m_QueryParser = new SQLQueryParser();
/**
*/
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;
/**
*
*/
private boolean m_InlineVariables = false;
/**
* This flag will be used to indicate if multiple result sets are
* supported from the database. If they are, then the metadata element is
* moved to insude the row-set element and multiple row-set elements may
* be included under the sql root element.
*/
private boolean m_IsMultipleResultsEnabled = false;
/**
* This flag will be used to indicate if database preparedstatements
* should be cached. This also controls if the Java statement object
* should be cached.
*/
private boolean m_IsStatementCachingEnabled = false;
/**
*/
public XConnection( )
{
}
/**
* Constructs a new XConnection and attempts to connect to a datasource as
* defined in the
* <code>connect(ExpressionContext exprContext, String connPoolName)</code>
* method.
* <code>org.apache.xalan.lib.sql.ConnectionPool</code> or a JNDI datasource.
*
* @param exprContext Context automatically passed from the XSLT sheet.
* @param name The name of the ConnectionPool or the JNDI DataSource path.
*
*/
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);
}
/**
* Returns an XConnection from either a user created
* <code>org.apache.xalan.lib.sql.ConnectionPool</code> or a JNDI datasource.
*
* This method first tries to resolve the passed name against
* <code>ConnectionPool</code>s registered with
* <code>ConnectionPoolManager</code>.
* If that fails, it attempts to find the name as a JNDI DataSource path.
*
* @param exprContext Context automatically passed from the XSLT sheet.
* @param name The name of the ConnectionPool or the JNDI DataSource path.
*
*/
public XBooleanStatic connect( ExpressionContext exprContext, String name )
{
try
{
m_ConnectionPool = m_PoolMgr.getPool(name);
if (m_ConnectionPool == null)
{
//Try to create a jndi source with the passed name
ConnectionPool pool = new JNDIConnectionPool(name);
if (pool.testConnection())
{
//JNDIConnectionPool seems good, so register it with the pool manager.
//Once registered, it will show up like other named ConnectionPool's,
//so the m_PoolMgr.getPool(name) method (above) will find it.
m_PoolMgr.registerPool(name, pool);
m_ConnectionPool = pool;
m_IsDefaultPool = false;
return new XBooleanStatic(true);
}
else
{
throw new IllegalArgumentException(
"Invalid ConnectionPool name or JNDI Datasource path: " + name);
}
}
else
{
m_IsDefaultPool = false;
return new XBooleanStatic(true);
}
}
catch (Exception e)
{
setError(e, exprContext);
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.
*
*/
public XBooleanStatic connect( ExpressionContext exprContext, String driver, String dbURL )
{
try
{
init(driver, dbURL, new Properties());
return new XBooleanStatic(true);
}
catch(SQLException e)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
catch (Exception e)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
}
/**
* @param exprContext
* @param protocolElem
*
*/
public XBooleanStatic connect( ExpressionContext exprContext, Element protocolElem )
{
try
{
initFromElement(protocolElem);
return new XBooleanStatic(true);
}
catch(SQLException e)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
catch (Exception e)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
}
/**
* @param exprContext
* @param list
*
*/
public XBooleanStatic connect( ExpressionContext exprContext, NodeList list )
{
try
{
initFromElement( (Element) list.item(0) );
return new XBooleanStatic(true);
}
catch(SQLException e)
{
setError(e, exprContext);
return new XBooleanStatic(false);
}
catch (Exception e)
{
setError(e,exprContext);
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.
*
*/
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)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
catch (Exception e)
{
setError(e,exprContext);
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".
*
*/
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)
{
setError(e,exprContext);
return new XBooleanStatic(false);
}
catch (Exception e)
{
setError(e, exprContext);
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
*
* @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
*
* @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
{
if ( con != null ) m_ConnectionPool.releaseConnection(con);
}
}
/**
* Allow the SQL Document to retrive a connection to be used
* to build the SQL Statement.
*/
public ConnectionPool getConnectionPool()
{
return m_ConnectionPool;
}
/**
* 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 )
{
SQLDocument doc = null;
try
{
if (DEBUG) System.out.println("pquery()");
// Build an Error Document, NOT Connected.
if ( null == m_ConnectionPool ) return null;
SQLQueryParser query =
m_QueryParser.parse
(this, queryString, SQLQueryParser.NO_INLINE_PARSER);
doc = SQLDocument.getNewDocument(exprContext);
doc.execute(this, query);
// also keep a local reference
m_OpenSQLDocuments.addElement(doc);
}
catch (Exception e)
{
// OK We had an error building the document, let try and grab the
// error information and clean up our connections.
if (DEBUG) System.out.println("exception in query()");
if (doc != null)
{
if (doc.hasErrors())
{
setError(e, doc, doc.checkWarnings());
}
doc.close(m_IsDefaultPool);
doc = null;
}
}
finally
{
if (DEBUG) System.out.println("leaving query()");
}
// Doc will be null if there was an error
return doc;
}
/**
* 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 )
{
return(pquery(exprContext, queryString, 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)
{
SQLDocument doc = null;
try
{
if (DEBUG) System.out.println("pquery()");
// Build an Error Document, NOT Connected.
if ( null == m_ConnectionPool ) return null;
SQLQueryParser query =
m_QueryParser.parse
(this, queryString, SQLQueryParser.NO_OVERRIDE);
// If we are not using the inline parser, then let add the data
// to the parser so it can populate the SQL Statement.
if ( !m_InlineVariables )
{
addTypeToData(typeInfo);
query.setParameters(m_ParameterList);
}
doc = SQLDocument.getNewDocument(exprContext);
doc.execute(this, query);
// also keep a local reference
m_OpenSQLDocuments.addElement(doc);
}
catch (Exception e)
{
// OK We had an error building the document, let try and grab the
// error information and clean up our connections.
if (DEBUG) System.out.println("exception in query()");
if (doc != null)
{
if (doc.hasErrors())
{
setError(e, doc, doc.checkWarnings());
}
// If we are using the Default Connection Pool, then
// force the connection pool to flush unused connections.
doc.close(m_IsDefaultPool);
doc = null;
}
}
finally
{
if (DEBUG) System.out.println("leaving query()");
}
// Doc will be null if there was an error
return doc;
}
/**
* The purpose of this routine is to force the DB cursor to skip forward
* N records. You should call this function after [p]query to help with
* pagination. i.e. Perfrom your select, then skip forward past the records
* you read previously.
*
* @param exprContext
* @param o
* @param value
*/
public void skipRec( ExpressionContext exprContext, Object o, int value )
{
SQLDocument sqldoc = null;
DTMNodeIterator nodei = null;
sqldoc = locateSQLDocument( exprContext, o);
if (sqldoc != null) sqldoc.skip(value);
}
private void addTypeToData(String typeInfo)
{
int indx;
if ( typeInfo != null && m_ParameterList != null )
{
// 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.setTypeName(value);
}
indx++;
}
}
}
/**
* Add an untyped value to the parameter list.
* @param value
*
*/
public void addParameter( String value )
{
addParameterWithType(value, null);
}
/**
* Add a typed parameter to the parameter list.
* @param value
* @param Type
*
*/
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
*
*/
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
*
*/
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
*
*/
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);
}
/**
*
*/
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.
*
* @deprecated Use setFeature("default-pool-enabled", "true");
*/
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
*
* @deprecated Use setFeature("default-pool-enabled", "false");
*/
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.
*
* @deprecated Use setFeature("streaming", "true");
*/
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.
*
* @deprecated Use setFeature("streaming", "false");
*/
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.
*
*/
public DTM getError( )
{
if ( m_FullErrors )
{
for ( int idx = 0 ; idx < m_OpenSQLDocuments.size() ; idx++ )
{
SQLDocument doc = (SQLDocument)m_OpenSQLDocuments.elementAt(idx);
SQLWarning warn = doc.checkWarnings();
if ( warn != null ) setError(null, doc, warn);
}
}
return(buildErrorDocument());
}
/**
* Close the connection to the data source.
*
* @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);
try
{
// If we are using the Default Connection Pool, then
// force the connection pool to flush unused connections.
d.close(m_IsDefaultPool);
}
catch (Exception se ) {}
m_OpenSQLDocuments.removeElementAt(0);
}
if ( null != m_Connection )
{
m_ConnectionPool.releaseConnection(m_Connection);
m_Connection = null;
}
if (DEBUG)
System.out.println("Exiting XConnection.close");
}
/**
* Close the connection to the data source. Only close the connections
* for a single document.
*
* @throws SQLException
*/
public void close(ExpressionContext exprContext, Object doc) throws SQLException
{
if (DEBUG)
System.out.println("Entering XConnection.close(" + doc + ")");
SQLDocument sqlDoc = locateSQLDocument(exprContext, doc);
if (sqlDoc != null)
{
// If we are using the Default Connection Pool, then
// force the connection pool to flush unused connections.
sqlDoc.close(m_IsDefaultPool);
m_OpenSQLDocuments.remove(sqlDoc);
}
}
/**
* When an SQL Document is returned as a DTM object, the XSL variable is actually
* assigned as a DTMIterator. This is a helper function that will allow you to get
* a reference to the original SQLDocument from the iterator.
*
* Original code submitted by
* Moraine Didier mailto://didier.moraine@winterthur.be
* @param doc
* @return
*/
private SQLDocument locateSQLDocument(ExpressionContext exprContext, Object doc)
{
try
{
if (doc instanceof DTMNodeIterator)
{
DTMNodeIterator dtmIter = (DTMNodeIterator)doc;
try
{
DTMNodeProxy root = (DTMNodeProxy)dtmIter.getRoot();
return (SQLDocument) root.getDTM();
}
catch (Exception e)
{
XNodeSet xNS = (XNodeSet)dtmIter.getDTMIterator();
DTMIterator iter = (DTMIterator)xNS.getContainedIter();
DTM dtm = iter.getDTM(xNS.nextNode());
return (SQLDocument)dtm;
}
}
/*
XNodeSet xNS = (XNodeSet)dtmIter.getDTMIterator();
OneStepIterator iter = (OneStepIterator)xNS.getContainedIter();
DTMManager aDTMManager = (DTMManager)iter.getDTMManager();
return (SQLDocument)aDTMManager.getDTM(xNS.nextNode());
*/
setError(new Exception("SQL Extension:close - Can Not Identify SQLDocument"), exprContext);
return null;
}
catch(Exception e)
{
setError(e, exprContext);
return null;
}
}
/**
* @param exprContext
* @param excp
*
*/
private SQLErrorDocument buildErrorDocument()
{
SQLErrorDocument eDoc = null;
if ( m_LastSQLDocumentWithError != null)
{
// @todo
// Do we need to do something with this ??
// m_Error != null || (m_FullErrors && m_Warning != null) )
ExpressionContext ctx = m_LastSQLDocumentWithError.getExpressionContext();
SQLWarning warn = m_LastSQLDocumentWithError.checkWarnings();
try
{
DTMManager mgr =
((XPathContext.XPathExpressionContext)ctx).getDTMManager();
DTMManagerDefault mgrDefault = (DTMManagerDefault) mgr;
int dtmIdent = mgrDefault.getFirstFreeDTMID();
eDoc = new SQLErrorDocument(
mgr, dtmIdent<<DTMManager.IDENT_DTM_NODE_BITS,
m_Error, warn, m_FullErrors);
// Register our document
mgrDefault.addDTM(eDoc, dtmIdent);
// Clear the error and warning.
m_Error = null;
m_LastSQLDocumentWithError = null;
}
catch(Exception e)
{
eDoc = null;
}
}
return(eDoc);
}
/**
* This is an internal version of Set Error that is called withen
* XConnection where there is no SQLDocument created yet. As in the
* Connect statement or creation of the ConnectionPool.
*/
public void setError(Exception excp,ExpressionContext expr)
{
try
{
ErrorListener listen = expr.getErrorListener();
if ( listen != null && excp != null )
{
listen.warning(
new TransformerException(excp.toString(),
expr.getXPathContext().getSAXLocator(), excp));
}
}
catch(Exception e) {}
}
/**
* Set an error and/or warning on this connection.
*
*/
public void setError(Exception excp, SQLDocument doc, SQLWarning warn)
{
ExpressionContext cont = doc.getExpressionContext();
m_LastSQLDocumentWithError = doc;
try
{
ErrorListener listen = cont.getErrorListener();
if ( listen != null && excp != null )
listen.warning(
new TransformerException(excp.toString(),
cont.getXPathContext().getSAXLocator(), excp));
if ( listen != null && warn != null )
{
listen.warning(new TransformerException(
warn.toString(), cont.getXPathContext().getSAXLocator(), warn));
}
// Assume there will be just one error, but perhaps multiple warnings.
if ( excp != null ) m_Error = excp;
if ( warn != null )
{
// Because the log may not have processed the previous warning yet
// we need to make a new one.
SQLWarning tw =
new SQLWarning(warn.getMessage(), warn.getSQLState(),
warn.getErrorCode());
SQLWarning nw = warn.getNextWarning();
while ( nw != null )
{
tw.setNextWarning(new SQLWarning(nw.getMessage(),
nw.getSQLState(), nw.getErrorCode()));
nw = nw.getNextWarning();
}
tw.setNextWarning(
new SQLWarning(warn.getMessage(), warn.getSQLState(),
warn.getErrorCode()));
// m_Warning = tw;
}
}
catch(Exception e)
{
//m_Error = null;
}
}
/**
* Set feature options for this XConnection.
* @param feature The name of the feature being set, currently supports (streaming, inline-variables, multiple-results, cache-statements, default-pool-enabled).
* @param setting The new setting for the specified feature, currently "true" is true and anything else is false.
*
*/
public void setFeature(String feature, String setting)
{
boolean value = false;
if ( "true".equalsIgnoreCase(setting) ) value = true;
if ( "streaming".equalsIgnoreCase(feature) )
{
m_IsStreamingEnabled = value;
}
else if ( "inline-variables".equalsIgnoreCase(feature) )
{
m_InlineVariables = value;
}
else if ( "multiple-results".equalsIgnoreCase(feature) )
{
m_IsMultipleResultsEnabled = value;
}
else if ( "cache-statements".equalsIgnoreCase(feature) )
{
m_IsStatementCachingEnabled = value;
}
else if ( "default-pool-enabled".equalsIgnoreCase(feature) )
{
m_DefaultPoolingEnabled = value;
if (m_ConnectionPool == null) return;
if (m_IsDefaultPool) return;
m_ConnectionPool.setPoolEnabled(value);
}
else if ( "full-errors".equalsIgnoreCase(feature) )
{
m_FullErrors = value;
}
}
/**
* Get feature options for this XConnection.
* @param feature The name of the feature to get the setting for.
* @return The setting of the specified feature. Will be "true" or "false" (null if the feature is not known)
*/
public String getFeature(String feature)
{
String value = null;
if ( "streaming".equalsIgnoreCase(feature) )
value = m_IsStreamingEnabled ? "true" : "false";
else if ( "inline-variables".equalsIgnoreCase(feature) )
value = m_InlineVariables ? "true" : "false";
else if ( "multiple-results".equalsIgnoreCase(feature) )
value = m_IsMultipleResultsEnabled ? "true" : "false";
else if ( "cache-statements".equalsIgnoreCase(feature) )
value = m_IsStatementCachingEnabled ? "true" : "false";
else if ( "default-pool-enabled".equalsIgnoreCase(feature) )
value = m_DefaultPoolingEnabled ? "true" : "false";
else if ( "full-errors".equalsIgnoreCase(feature) )
value = m_FullErrors ? "true" : "false";
return(value);
}
/**
*
*/
protected void finalize( )
{
if (DEBUG) System.out.println("In XConnection, finalize");
try
{
close();
}
catch(Exception e)
{
// Empty We are final Anyway
}
}
}