com.microsoft.sqlserver.jdbc.SQLServerSQLXML Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mssql-jdbc Show documentation
Show all versions of mssql-jdbc Show documentation
Microsoft JDBC 4.2 Driver for SQL Server.
The Azure Key Vault feature in Microsoft JDBC Driver for SQL Server depends on
Azure SDK for JAVA and Azure Active Directory Library For Java.
//---------------------------------------------------------------------------------------------------------------------------------
// File: SQLServerSQLXML.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
package com.microsoft.sqlserver.jdbc;
import java.sql.*;
import java.io.*;
import javax.xml.XMLConstants;
import javax.xml.parsers.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.*;
import org.w3c.dom.*;
import java.text.*;
import org.xml.sax.*;
import javax.xml.transform.stax.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* SQLServerSQLXML represents an XML object and implements a java.sql.SQLXML.
*/
final class SQLServerSQLXML implements java.sql.SQLXML
{
// Connection that created this SQLXML only set when created for setting data
private final SQLServerConnection con;
// Contents null means this is a setter.
private final PLPXMLInputStream contents;
private final InputStreamGetterArgs getterArgs;
private final TypeInfo typeInfo;
private boolean isUsed=false;
private boolean isFreed = false;
static private final java.util.logging.Logger logger =
java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerSQLXML");
// Setter values are held in different forms depending on which setter is used only one setter can
// be used at one time.
private ByteArrayOutputStreamToInputStream outputStreamValue;
private Document docValue;
private String strValue;
// End of setter values
static private int baseID = 0; // Unique id generator for each instance (used for logging).
final private String traceID;
final public String toString()
{
return traceID;
}
// Returns unique id for each instance.
private synchronized static int nextInstanceID()
{
baseID++;
return baseID;
}
// This method is used to get the value the user has set
// This is used at the execution time when sending the data to the server
// possible optimization transform DOM straight to tds
InputStream getValue() throws SQLServerException
{
checkClosed();
// the user should have called one of the setter methods, the setter methods "use" the object and write some value.
// Note just calling one of the setters is enough.
if (!isUsed )
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_noDataXML"), null, true);
assert null == contents;
ByteArrayInputStream o=null;
if(null != outputStreamValue)
{
o = outputStreamValue.getInputStream();
assert null == docValue;
assert null == strValue;
}
else
if (null != docValue)
{
assert null ==outputStreamValue;
assert null == strValue;
ByteArrayOutputStreamToInputStream strm = new ByteArrayOutputStreamToInputStream();
// Need to beat a stream out of docValue
TransformerFactory factory;
Writer wr=null;
try
{
factory = TransformerFactory.newInstance();
factory.newTransformer().transform(new DOMSource(docValue), new StreamResult(strm));
}
catch (javax.xml.transform.TransformerException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
o = strm.getInputStream();
try {
strm.close();
} catch (IOException e) {
throw new SQLServerException(null, e.getMessage(), null, 0, true);
}
}
else
{
assert null ==outputStreamValue;
assert null == docValue;
assert null !=strValue;
try
{
o = new ByteArrayInputStream(strValue.getBytes(Encoding.UNICODE.charsetName()));
}
catch (UnsupportedEncodingException ex)
{
throw new SQLServerException(null, ex.getMessage(), null, 0, true);
}
}
assert null != o;
isFreed = true; // we have consumed the data
return o;
}
// To be used in the createSQLXML call
SQLServerSQLXML(SQLServerConnection connection)
{
contents =null;
traceID = " SQLServerSQLXML:" + nextInstanceID();
con = connection;
if(logger.isLoggable(java.util.logging.Level.FINE))
logger.fine(toString() + " created by (" + connection.toString() + ")");
getterArgs = null; // make the compiler happy
typeInfo = null;
}
SQLServerSQLXML(InputStream stream, InputStreamGetterArgs getterArgs, TypeInfo typeInfo) throws SQLServerException
{
traceID = " SQLServerSQLXML:" + nextInstanceID();
contents = (PLPXMLInputStream)stream;
this.con = null;
this.getterArgs = getterArgs;
this.typeInfo = typeInfo;
if(logger.isLoggable(java.util.logging.Level.FINE))
logger.fine(toString() + " created by (null connection)");
}
InputStream getStream()
{
return contents;
}
public void free() throws SQLException
{
if(!isFreed)
{
isFreed = true;
if(null != contents)
{
try
{
contents.close();
}
catch (IOException e)
{
SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
}
}
}
}
private void checkClosed() throws SQLServerException
{
if (isFreed || (null != con && con.isClosed()) )
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_isFreed"));
SQLServerException.makeFromDriverError(con, null, form.format(new Object[] {"SQLXML"}), null, true);
}
}
private void checkReadXML() throws SQLException
{
if (null == contents)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_writeOnlyXML"), null, true);
if(isUsed)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_dataHasBeenReadXML"), null, true);
try
{
contents.checkClosed();
}
catch (IOException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_isFreed"));
SQLServerException.makeFromDriverError(con, null, form.format(new Object[] {"SQLXML"}), null, true);
}
}
void checkWriteXML() throws SQLException
{
if (null != contents)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_readOnlyXML"), null, true);
if(isUsed)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_dataHasBeenSetXML"), null, true);
}
/**
* Return an input stream to read data from this SQLXML
* @throws SQLServerException
* @return the input stream to that contains the SQLXML data
*/
public InputStream getBinaryStream() throws SQLException
{
checkClosed();
checkReadXML();
isUsed = true;
return contents;
}
/**
* Retrieves a stream that can be used to write to the SQLXML value that this SQLXML object represents
* The user has to write the BOM for binary streams.
* @param pos the position in the SQLXML value at which to start writing
* @throws SQLException
* @return OutputStream
*/
public java.io.OutputStream setBinaryStream() throws SQLException
{
checkClosed();
checkWriteXML();
isUsed = true;
outputStreamValue = new ByteArrayOutputStreamToInputStream();
return outputStreamValue;
}
public java.io.Writer setCharacterStream() throws SQLException
{
checkClosed();
checkWriteXML();
isUsed = true;
outputStreamValue = new ByteArrayOutputStreamToInputStream();
java.io.Writer wrt=null;
try
{
wrt = new OutputStreamWriter(outputStreamValue, Encoding.UNICODE.charsetName());
}
catch (UnsupportedEncodingException ex)
{
throw new SQLServerException(null, ex.getMessage(), null, 0, true);
}
return wrt;
}
public Reader getCharacterStream() throws SQLException
{
checkClosed();
checkReadXML();
isUsed = true;
StreamType type = StreamType.CHARACTER;
InputStreamGetterArgs newArgs = new InputStreamGetterArgs(
type,
getterArgs.isAdaptive,
getterArgs.isStreaming,
getterArgs.logContext);
// Skip the BOM bytes from the plpinputstream we do not need it for the conversion
assert null != contents;
// Read two bytes to eat BOM
try
{
contents.read();
contents.read();
}
catch (IOException e)
{
SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
}
Reader rd = (Reader)DDC.convertStreamToObject(contents, typeInfo,type.getJDBCType(), newArgs);
return rd;
}
public String getString() throws SQLException
{
checkClosed();
checkReadXML();
isUsed = true;
assert null != contents;
// Read two bytes to eat BOM
try
{
contents.read();
contents.read();
}
catch (IOException e)
{
SQLServerException.makeFromDriverError(null, null, e.getMessage(), null, true);
}
byte byteContents[] = contents.getBytes();
String ret = null;
try
{
ret = new String(byteContents,0, byteContents.length, Encoding.UNICODE.charsetName() );
}
catch (UnsupportedEncodingException ex)
{
throw new SQLServerException(null, ex.getMessage(), null, 0, true);
}
return ret;
}
public void setString(String value) throws SQLException
{
checkClosed();
checkWriteXML();
isUsed = true;
if(null == value)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_cantSetNull"), null, true);
strValue = value;
}
// Support the following DOMSource, SAXSource, StAX and Stream. Also, null means default which is stream source
public T getSource(Class iface) throws SQLException
{
checkClosed();
checkReadXML();
if(null == iface)
{
// The compiler does not allow setting the null iface to StreamSource.class hence
// this cast is needed, the cast is safe.
@SuppressWarnings("unchecked") T src = (T)getSourceInternal(StreamSource.class);
return src;
}
else
return getSourceInternal(iface);
}
T getSourceInternal(Class iface) throws SQLException
{
isUsed = true;
T src =null;
if(DOMSource.class == iface)
{
src = iface.cast(getDOMSource());
}else
if(SAXSource.class == iface)
{
src = iface.cast(getSAXSource());
}
else
if(StAXSource.class == iface)
{
src = iface.cast(getStAXSource());
}
else
if(StreamSource.class == iface)
{
src = iface.cast(new StreamSource(contents));
}
else
// Do not support this type
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_notSupported"), null, true);
return src;
}
public T setResult(Class resultClass) throws SQLException
{
checkClosed();
checkWriteXML();
if(null == resultClass)
{
// The compiler does not allow setting the null resultClass to StreamResult.class hence
// this cast is needed, the cast is safe.
@SuppressWarnings("unchecked") T result = (T)setResultInternal(StreamResult.class);
return result;
}
else
return setResultInternal(resultClass);
}
T setResultInternal(Class resultClass) throws SQLException
{
isUsed = true;
T result=null;
if(DOMResult.class == resultClass)
{
result = resultClass.cast(getDOMResult());
}
else
if(SAXResult.class == resultClass)
{
result = resultClass.cast(getSAXResult());
}
else
if(StAXResult.class == resultClass)
{
result = resultClass.cast(getStAXResult());
}else
if(StreamResult.class == resultClass)
{
outputStreamValue = new ByteArrayOutputStreamToInputStream();
result = resultClass.cast(new StreamResult(outputStreamValue));
}
else
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString("R_notSupported"), null, true);
return result;
}
// Supporting functions
private final DOMSource getDOMSource() throws SQLException
{
Document document = null;
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try
{
//Secure feature processing is false by default for IBM parser
//For all others, it is true by default.
//So, setting it explicitly for all, irrespective of the JVM.
//This limits the expansion of entities.
//The limit is implementation specific. For IBM it's 100,000
//whereas for oracle it is 64,000.
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
builder = factory.newDocumentBuilder();
//set an entity resolver to disable parsing of external entities
builder.setEntityResolver(new SQLServerEntityResolver());
try
{
document = builder.parse(contents);
}
catch (IOException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorReadingStream"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), "", true);
}
DOMSource inputSource = new DOMSource(document);
return inputSource;
} catch (ParserConfigurationException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
catch (SAXException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToParseXML"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
return null;
}
private final SAXSource getSAXSource() throws SQLException
{
try
{
InputSource src = new InputSource(contents);
XMLReader reader = XMLReaderFactory.createXMLReader();
SAXSource saxSource = new SAXSource(reader, src);
return saxSource;
}
catch (SAXException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToParseXML"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
return null;
}
private final StAXSource getStAXSource() throws SQLException
{
XMLInputFactory factory = XMLInputFactory.newInstance();
try
{
XMLStreamReader r = factory.createXMLStreamReader(contents);
StAXSource result = new StAXSource(r);
return result;
} catch (XMLStreamException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
return null;
}
private final StAXResult getStAXResult() throws SQLException
{
XMLOutputFactory factory = XMLOutputFactory.newInstance();
outputStreamValue = new ByteArrayOutputStreamToInputStream();
try
{
XMLStreamWriter r = factory.createXMLStreamWriter(outputStreamValue);
StAXResult result = new StAXResult(r);
return result;
} catch (XMLStreamException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
return null;
}
private final SAXResult getSAXResult() throws SQLException
{
TransformerHandler handler=null;
try
{
SAXTransformerFactory stf =
(SAXTransformerFactory)TransformerFactory.newInstance();
handler = stf.newTransformerHandler();
}
catch (TransformerConfigurationException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
catch (ClassCastException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
outputStreamValue = new ByteArrayOutputStreamToInputStream();
handler.setResult(new StreamResult(outputStreamValue));
SAXResult result= new SAXResult(handler);
return result;
}
private final DOMResult getDOMResult() throws SQLException
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
assert null == outputStreamValue;
try
{
builder = factory.newDocumentBuilder();
docValue = builder.newDocument();
DOMResult result = new DOMResult(docValue);
return result;
} catch (ParserConfigurationException e)
{
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noParserSupport"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
return null;
}
}
// We use this class to convert the byte information we have in the string to
// an inputstream which is used when sending to the info to the server
final class ByteArrayOutputStreamToInputStream extends ByteArrayOutputStream
{
ByteArrayInputStream getInputStream() throws SQLServerException
{
ByteArrayInputStream is = new ByteArrayInputStream(buf, 0, count);
return is;
}
}
//Resolves External entities in an XML with inline DTDs to empty string
final class SQLServerEntityResolver implements EntityResolver
{
public InputSource resolveEntity (String publicId, String systemId)
{
return new InputSource(new StringReader(""));
}
}