diff --git a/.gitignore b/.gitignore
index acb9b8d91..b8095970d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,8 @@ local.properties
.settings/
.gradle/
.loadpath
+outdated-dependencies.txt
+pom.xml.versionBackup
# External tool builders
.externalToolBuilders/
diff --git a/.travis.yml b/.travis.yml
index 47c919414..a9a2d8f11 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,13 @@
language: java
jdk:
- - oraclejdk8
+ - oraclejdk9
+addons:
+ apt:
+ packages:
+ - oracle-java9-installer
+
services:
- docker
@@ -29,7 +34,7 @@ install:
- keytool -importkeystore -destkeystore clientcert.jks -deststorepass password -srckeystore identity.p12 -srcstoretype PKCS12 -srcstorepass password
- keytool -list -v -keystore clientcert.jks -storepass "password" > JavaKeyStore.txt
- cd ..
- - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild41
+ - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild43
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42
before_script:
@@ -39,6 +44,6 @@ before_script:
script:
- docker ps -a
-##Test for JDBC Specification 41 & 42 and submit coverage report.
- - mvn test -B -Pbuild41 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC41
+##Test for JDBC Specification 43 & 42 and submit coverage report.
+ - mvn test -B -Pbuild41 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC43
- mvn test -B -Pbuild42 jacoco:report && bash <(curl -s https://codecov.io/bash) -cF JDBC42
diff --git a/README.md b/README.md
index 088b6c278..6d2d0a746 100644
--- a/README.md
+++ b/README.md
@@ -36,19 +36,19 @@ What's coming next? We will look into adding a more comprehensive set of tests,
## Build
### Prerequisites
-* Java 8
+* Java 9
* [Maven](http://maven.apache.org/download.cgi)
* An instance of SQL Server or Azure SQL Database that you can connect to.
### Build the JAR files
Maven builds automatically trigger a set of verification tests to run. For these tests to pass, you will first need to add an environment variable in your system called `mssql_jdbc_test_connection_properties` to provide the [correct connection properties](https://msdn.microsoft.com/en-us/library/ms378428(v=sql.110).aspx) for your SQL Server or Azure SQL Database instance.
-To build the jar files, you must use Java 8 with Maven. You can choose to build a JDBC 4.1 compliant jar file (for use with JRE 7) and/or a JDBC 4.2 compliant jar file (for use with JRE 8).
+To build the jar files, you must use Java 9 with Maven. You can choose to build a JDBC 4.3 compliant jar file (for use with JRE 9) and/or a JDBC 4.2 compliant jar file (for use with JRE 8).
* Maven:
1. If you have not already done so, add the environment variable `mssql_jdbc_test_connection_properties` in your system with the connection properties for your SQL Server or SQL DB instance.
- 2. Run one of the commands below to build a JDBC 4.1 compliant jar or JDBC 4.2 compliant jar in the \target directory.
- * Run `mvn install -Pbuild41`. This creates JDBC 4.1 compliant jar in \target directory
+ 2. Run one of the commands below to build a JDBC 4.3 compliant jar or JDBC 4.2 compliant jar in the \target directory.
+ * Run `mvn install -Pbuild43`. This creates JDBC 4.3 compliant jar in \target directory
* Run `mvn install -Pbuild42`. This creates JDBC 4.2 compliant jar in \target directory
**NOTE**: Beginning release v6.1.7, we will no longer be maintaining the existing [Gradle build script](build.gradle) and it will be left in the repository for reference. Please refer to issue [#62](https://github.com/Microsoft/mssql-jdbc/issues/62) for this decision.
diff --git a/appveyor.yml b/appveyor.yml
index cbd4229fe..8caae887f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -32,9 +32,9 @@ build_script:
- keytool -importkeystore -srckeystore cert.pfx -srcstoretype pkcs12 -destkeystore clientcert.jks -deststoretype JKS -srcstorepass password -deststorepass password
- keytool -list -v -keystore clientcert.jks -storepass "password" > JavaKeyStore.txt
- cd..
- - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild41
- - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42
+ # - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild41
+ # - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -Pbuild42
-test_script:
- - mvn test -B -Pbuild41
- - mvn test -B -Pbuild42
+#test_script:
+# - mvn test -B -Pbuild41
+# - mvn test -B -Pbuild42
diff --git a/maven-version-rules.xml b/maven-version-rules.xml
new file mode 100644
index 000000000..6d126d278
--- /dev/null
+++ b/maven-version-rules.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ (?i).*Alpha(?:-?\d+)?
+ (?i).*Beta(?:-?\d+)?
+ (?i).*-B(?:-?\d+)?
+ (?i).*RC(?:-?\d+)?
+ (?i).*EA(?:-?\d+)?
+ (?i).*SNAPSHOT(?:-?\d+)?
+
+ (?i).*M(?:-?\d+)?
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1caec83f8..c6e459c4e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.microsoft.sqlserver
mssql-jdbc
- 6.3.6.${jreVersion}-preview
+ 6.4.0-SNAPSHOT.${jreVersion}
jar
Microsoft JDBC Driver for SQL Server
@@ -55,7 +55,7 @@
com.microsoft.azure
adal4j
- 1.3.0
+ 1.4.0
true
@@ -118,7 +118,7 @@
com.zaxxer
HikariCP
- 2.6.1
+ 2.7.4
test
@@ -139,10 +139,10 @@
- build41
+ build42
- jre7
+ jre8
@@ -152,10 +152,10 @@
3.6.0
- **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java
+ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java
- 1.7
- 1.7
+ 1.8
+ 1.8
@@ -167,21 +167,19 @@
${project.build.outputDirectory}/META-INF/MANIFEST.MF
-
-
+
-
+
- build42
-
+ build43
true
- jre8
+ jre9
@@ -191,10 +189,10 @@
3.6.0
- **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java
+ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java
- 1.8
- 1.8
+ 9
+ 9
@@ -279,7 +277,7 @@
org.apache.felix
maven-bundle-plugin
- 3.2.0
+ 3.3.0
true
@@ -301,7 +299,7 @@
org.apache.maven.plugins
maven-javadoc-plugin
- 2.10.4
+ 3.0.0-M1
true
@@ -336,6 +334,16 @@
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ true
+
+ outdated-dependencies.txt
+ file:///${session.executionRootDirectory}/maven-version-rules.xml
+
+
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java
index d86d481f4..112aac152 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java
@@ -41,6 +41,10 @@ final class AuthenticationJNI extends SSPIAuthentication {
static int GetMaxSSPIBlobSize() {
return sspiBlobMaxlen;
}
+
+ static boolean isDllLoaded() {
+ return enabled;
+ }
static {
UnsatisfiedLinkError temp = null;
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java
index 2fa7d0afe..1e90ef726 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java
@@ -232,7 +232,7 @@ static final Object convertFloatToObject(float floatVal,
return new BigDecimal(Float.toString(floatVal));
case FLOAT:
case DOUBLE:
- return ((Float)floatVal).doubleValue();
+ return (Float.valueOf(floatVal)).doubleValue();
case BINARY:
return convertIntToBytes(Float.floatToRawIntBits(floatVal), 4);
default:
@@ -275,7 +275,7 @@ static final Object convertDoubleToObject(double doubleVal,
case DOUBLE:
return doubleVal;
case REAL:
- return ((Double)doubleVal).floatValue();
+ return (Double.valueOf(doubleVal)).floatValue();
case INTEGER:
return (int) doubleVal;
case SMALLINT: // small and tinyint returned as short
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java b/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java
index 0e905bc7d..cdfe4fb83 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/FailOverInfo.java
@@ -71,7 +71,7 @@ private void setupInfo(SQLServerConnection con) throws SQLServerException {
instancePort = con.getInstancePort(failoverPartner, instanceValue);
try {
- portNumber = new Integer(instancePort);
+ portNumber = Integer.parseInt(instancePort);
}
catch (NumberFormatException e) {
// Should not get here as the server should give a proper port number anyway.
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
index d6b28a14a..82513ea17 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
@@ -73,7 +73,7 @@
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-import javax.xml.bind.DatatypeConverter;
+import java.nio.Buffer;
final class TDS {
// TDS protocol versions
@@ -3107,7 +3107,7 @@ boolean isEOMSent() {
void preparePacket() throws SQLServerException {
if (tdsChannel.isLoggingPackets()) {
Arrays.fill(logBuffer.array(), (byte) 0xFE);
- logBuffer.clear();
+ ((Buffer)logBuffer).clear();
}
// Write a placeholder packet header. This will be replaced
@@ -3183,8 +3183,8 @@ void startMessage(TDSCommand command,
currentPacketSize = negotiatedPacketSize;
}
- socketBuffer.position(socketBuffer.limit());
- stagingBuffer.clear();
+ ((Buffer) socketBuffer).position(((Buffer) socketBuffer).limit());
+ ((Buffer)stagingBuffer).clear();
preparePacket();
writeMessageHeader();
@@ -3226,7 +3226,7 @@ void writeByte(byte value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.put(value);
else
- logBuffer.position(logBuffer.position() + 1);
+ ((Buffer)logBuffer).position(((Buffer)logBuffer).position() + 1);
}
}
else {
@@ -3253,7 +3253,7 @@ void writeChar(char value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.putChar(value);
else
- logBuffer.position(logBuffer.position() + 2);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 2);
}
}
else {
@@ -3269,7 +3269,7 @@ void writeShort(short value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.putShort(value);
else
- logBuffer.position(logBuffer.position() + 2);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 2);
}
}
else {
@@ -3285,7 +3285,7 @@ void writeInt(int value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.putInt(value);
else
- logBuffer.position(logBuffer.position() + 4);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 4);
}
}
else {
@@ -3317,7 +3317,7 @@ void writeDouble(double value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.putDouble(value);
else
- logBuffer.position(logBuffer.position() + 8);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 8);
}
}
else {
@@ -3695,7 +3695,7 @@ void writeLong(long value) throws SQLServerException {
if (dataIsLoggable)
logBuffer.putLong(value);
else
- logBuffer.position(logBuffer.position() + 8);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + 8);
}
}
else {
@@ -3738,7 +3738,7 @@ void writeBytes(byte[] value,
if (dataIsLoggable)
logBuffer.put(value, offset + bytesWritten, bytesToWrite);
else
- logBuffer.position(logBuffer.position() + bytesToWrite);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + bytesToWrite);
}
bytesWritten += bytesToWrite;
@@ -3765,7 +3765,7 @@ void writeWrappedBytes(byte value[],
if (dataIsLoggable)
logBuffer.put(value, 0, remaining);
else
- logBuffer.position(logBuffer.position() + remaining);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + remaining);
}
}
@@ -3778,7 +3778,7 @@ void writeWrappedBytes(byte value[],
if (dataIsLoggable)
logBuffer.put(value, remaining, valueLength - remaining);
else
- logBuffer.position(logBuffer.position() + remaining);
+ ((Buffer)logBuffer).position( ((Buffer)logBuffer).position() + remaining);
}
}
@@ -4100,7 +4100,7 @@ private void writePacket(int tdsMessageStatus) throws SQLServerException {
}
private void writePacketHeader(int tdsMessageStatus) {
- int tdsMessageLength = stagingBuffer.position();
+ int tdsMessageLength = ((Buffer)stagingBuffer).position();
++packetNum;
// Write the TDS packet header back at the start of the staging buffer
@@ -4128,13 +4128,13 @@ private void writePacketHeader(int tdsMessageStatus) {
void flush(boolean atEOM) throws SQLServerException {
// First, flush any data left in the socket buffer.
- tdsChannel.write(socketBuffer.array(), socketBuffer.position(), socketBuffer.remaining());
- socketBuffer.position(socketBuffer.limit());
+ tdsChannel.write(socketBuffer.array(), ((Buffer)socketBuffer).position(), socketBuffer.remaining());
+ ((Buffer)socketBuffer).position(((Buffer)socketBuffer).limit());
// If there is data in the staging buffer that needs to be written
// to the socket, the socket buffer is now empty, so swap buffers
// and start writing data from the staging buffer.
- if (stagingBuffer.position() >= TDS_PACKET_HEADER_SIZE) {
+ if (((Buffer)stagingBuffer).position() >= TDS_PACKET_HEADER_SIZE) {
// Swap the packet buffers ...
ByteBuffer swapBuffer = stagingBuffer;
stagingBuffer = socketBuffer;
@@ -4146,14 +4146,14 @@ void flush(boolean atEOM) throws SQLServerException {
// We need to use flip() rather than rewind() here so that
// the socket buffer's limit is properly set for the last
// packet, which may be shorter than the other packets.
- socketBuffer.flip();
- stagingBuffer.clear();
+ ((Buffer)socketBuffer).flip();
+ ((Buffer)stagingBuffer).clear();
// If we are logging TDS packets then log the packet we're about
// to send over the wire now.
if (tdsChannel.isLoggingPackets()) {
- tdsChannel.logPacket(logBuffer.array(), 0, socketBuffer.limit(),
- this.toString() + " sending packet (" + socketBuffer.limit() + " bytes)");
+ tdsChannel.logPacket(logBuffer.array(), 0, ((Buffer) socketBuffer).limit(),
+ this.toString() + " sending packet (" + ((Buffer) socketBuffer).limit() + " bytes)");
}
// Prepare for the next packet
@@ -4161,8 +4161,8 @@ void flush(boolean atEOM) throws SQLServerException {
preparePacket();
// Finally, start sending data from the new socket buffer.
- tdsChannel.write(socketBuffer.array(), socketBuffer.position(), socketBuffer.remaining());
- socketBuffer.position(socketBuffer.limit());
+ tdsChannel.write(socketBuffer.array(), ((Buffer)socketBuffer).position(), socketBuffer.remaining());
+ ((Buffer)socketBuffer).position( ((Buffer)socketBuffer).limit());
}
}
@@ -4625,7 +4625,7 @@ void writeTVPRows(TVP value) throws SQLServerException {
if (con.equals(src_stmt.getConnection()) && 0 != resultSetServerCursorId) {
cachedTVPHeaders = ByteBuffer.allocate(stagingBuffer.capacity()).order(stagingBuffer.order());
- cachedTVPHeaders.put(stagingBuffer.array(), 0, stagingBuffer.position());
+ cachedTVPHeaders.put(stagingBuffer.array(), 0, ((Buffer)stagingBuffer).position());
cachedCommand = this.command;
@@ -4651,9 +4651,9 @@ void writeTVPRows(TVP value) throws SQLServerException {
if (tdsWritterCached) {
command = cachedCommand;
- stagingBuffer.clear();
- logBuffer.clear();
- writeBytes(cachedTVPHeaders.array(), 0, cachedTVPHeaders.position());
+ ((Buffer)stagingBuffer).clear();
+ ((Buffer)logBuffer).clear();
+ writeBytes(cachedTVPHeaders.array(), 0, ((Buffer)cachedTVPHeaders).position());
}
Object[] rowData = value.getRowData();
@@ -4952,7 +4952,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength)
isShortValue = columnPair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES;
isNull = (null == currentObject);
if (currentObject instanceof String)
- dataLength = isNull ? 0 : (toByteArray(currentObject.toString())).length;
+ dataLength = isNull ? 0 : (ParameterUtils.HexToBin(currentObject.toString())).length;
else
dataLength = isNull ? 0 : ((byte[]) currentObject).length;
if (!isShortValue) {
@@ -4971,7 +4971,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength)
if (dataLength > 0) {
writeInt(dataLength);
if (currentObject instanceof String)
- writeBytes(toByteArray(currentObject.toString()));
+ writeBytes(ParameterUtils.HexToBin(currentObject.toString()));
else
writeBytes((byte[]) currentObject);
}
@@ -4985,7 +4985,7 @@ else if (DataTypes.UNKNOWN_STREAM_LENGTH == dataLength)
else {
writeShort((short) dataLength);
if (currentObject instanceof String)
- writeBytes(toByteArray(currentObject.toString()));
+ writeBytes(ParameterUtils.HexToBin(currentObject.toString()));
else
writeBytes((byte[]) currentObject);
}
@@ -5022,9 +5022,6 @@ private void writeTVPSqlVariantHeader(int length,
writeByte(probBytes);
}
- private static byte[] toByteArray(String s) {
- return DatatypeConverter.parseHexBinary(s);
- }
void writeTVPColumnMetaData(TVP value) throws SQLServerException {
boolean isShortValue;
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java
new file mode 100644
index 000000000..1689cde87
--- /dev/null
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java
@@ -0,0 +1,19 @@
+package com.microsoft.sqlserver.jdbc;
+
+import java.sql.ShardingKey;
+
+public interface ISQLServerConnection43 extends ISQLServerConnection {
+
+ public void setShardingKey(ShardingKey shardingKey) throws SQLServerException;
+
+ public void setShardingKey(ShardingKey shardingKey,
+ ShardingKey superShardingKey) throws SQLServerException;
+
+ public boolean setShardingKeyIfValid(ShardingKey shardingKey,
+ int timeout) throws SQLServerException;
+
+ public boolean setShardingKeyIfValid(ShardingKey shardingKey,
+ ShardingKey superShardingKey,
+ int timeout) throws SQLServerException;
+
+}
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java
index 2667d5af0..1a2513816 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java
@@ -11,6 +11,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
@@ -182,7 +183,7 @@ private boolean encodeChars() throws IOException {
}
else {
// Flip the buffer to be ready for put (reader read) operations.
- rawChars.clear();
+ ((Buffer)rawChars).clear();
}
// Try to fill up the raw character buffer by reading available characters
@@ -194,7 +195,7 @@ private boolean encodeChars() throws IOException {
// - the reader throws any kind of Exception (driver throws an IOException)
// - the reader violates its interface contract (driver throws an IOException)
while (rawChars.hasRemaining()) {
- int lastPosition = rawChars.position();
+ int lastPosition = ((Buffer)rawChars).position();
int charsRead = 0;
// Try reading from the app-supplied Reader
@@ -218,7 +219,7 @@ private boolean encodeChars() throws IOException {
if (-1 == charsRead) {
// If the reader violates its interface contract then throw an exception.
- if (rawChars.position() != lastPosition)
+ if (((Buffer)rawChars).position() != lastPosition)
throw new IOException(SQLServerException.getErrString("R_streamReadReturnedInvalidValue"));
// Check that the reader has returned exactly the amount of data we expect
@@ -228,7 +229,7 @@ private boolean encodeChars() throws IOException {
}
// If there are no characters left to encode then we're done.
- if (0 == rawChars.position()) {
+ if (0 == ((Buffer)rawChars).position()) {
rawChars = null;
atEndOfStream = true;
return false;
@@ -241,7 +242,7 @@ private boolean encodeChars() throws IOException {
assert charsRead > 0;
// If the reader violates its interface contract then throw an exception.
- if (charsRead != rawChars.position() - lastPosition)
+ if (charsRead != ((Buffer)rawChars).position() - lastPosition)
throw new IOException(SQLServerException.getErrString("R_streamReadReturnedInvalidValue"));
// Check that the reader isn't trying to return more data than we expect
@@ -255,7 +256,7 @@ private boolean encodeChars() throws IOException {
// The raw character buffer may now have characters available for encoding.
// Flip the buffer back to be ready for get (charset encode) operations.
- rawChars.flip();
+ ((Buffer)rawChars).flip();
}
// If the raw character buffer remains empty, despite our efforts to (re)populate it,
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java
index e3e10dd06..096543ffd 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java
@@ -1,11 +1,15 @@
package com.microsoft.sqlserver.jdbc;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.text.MessageFormat;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.logging.Level;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationException;
@@ -15,10 +19,13 @@
class SQLServerADAL4JUtils {
+ static final private java.util.logging.Logger adal4jLogger = java.util.logging.Logger
+ .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerADAL4JUtils");
+
static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo,
- String user,
- String password,
- String authenticationString) throws SQLServerException {
+ String user,
+ String password,
+ String authenticationString) throws SQLServerException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
try {
AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService);
@@ -42,7 +49,8 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo,
String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n");
AuthenticationException correctedAuthenticationException = new AuthenticationException(correctedErrorMessage);
- // SQLServerException is caused by ExecutionException, which is caused by AuthenticationException
+ // SQLServerException is caused by ExecutionException, which is caused by
+ // AuthenticationException
// to match the exception tree before error message correction
ExecutionException correctedExecutionException = new ExecutionException(correctedAuthenticationException);
@@ -52,4 +60,57 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo,
executorService.shutdown();
}
}
+
+ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo,
+ String authenticationString) throws SQLServerException {
+ ExecutorService executorService = Executors.newFixedThreadPool(1);
+
+ try {
+ // principal name does not matter, what matters is the realm name
+ // it gets the username in principal_name@realm_name format
+ KerberosPrincipal kerberosPrincipal = new KerberosPrincipal("username");
+ String username = kerberosPrincipal.getName();
+
+ if (adal4jLogger.isLoggable(Level.FINE)) {
+ adal4jLogger.fine(adal4jLogger.toString() + " realm name is:" + kerberosPrincipal.getRealm());
+ }
+
+ AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService);
+ Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID,
+ username, null, null);
+
+ AuthenticationResult authenticationResult = future.get();
+ SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate());
+
+ return fedAuthToken;
+ }
+ catch (InterruptedException | IOException e) {
+ throw new SQLServerException(e.getMessage(), e);
+ }
+ catch (ExecutionException e) {
+ MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution"));
+ Object[] msgArgs = {"", authenticationString};
+
+ if (null == e.getCause() || null == e.getCause().getMessage()) {
+ // the case when Future's outcome has no AuthenticationResult but exception
+ throw new SQLServerException(form.format(msgArgs), null);
+ }
+ else {
+ // the cause error message uses \\n\\r which does not give correct format
+ // change it to \r\n to provide correct format
+ String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n");
+ AuthenticationException correctedAuthenticationException = new AuthenticationException(correctedErrorMessage);
+
+ // SQLServerException is caused by ExecutionException, which is caused by
+ // AuthenticationException
+ // to match the exception tree before error message correction
+ ExecutionException correctedExecutionException = new ExecutionException(correctedAuthenticationException);
+
+ throw new SQLServerException(form.format(msgArgs), null, 0, correctedExecutionException);
+ }
+ }
+ finally {
+ executorService.shutdown();
+ }
+ }
}
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java
index 8f71ec6b6..3a170da2d 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java
@@ -12,8 +12,7 @@
import java.text.MessageFormat;
import java.util.concurrent.ConcurrentHashMap;
-
-import javax.xml.bind.DatatypeConverter;
+import java.util.Base64;
/**
* Factory for SQLServerAeadAes256CbcHmac256Algorithm
@@ -38,7 +37,7 @@ SQLServerEncryptionAlgorithm create(SQLServerSymmetricKey columnEncryptionKey,
}
StringBuilder factoryKeyBuilder = new StringBuilder();
- factoryKeyBuilder.append(DatatypeConverter.printBase64Binary(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes()));
+ factoryKeyBuilder.append(Base64.getEncoder().encodeToString(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes()));
factoryKeyBuilder.append(":");
factoryKeyBuilder.append(encryptionType);
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java
index 458801d19..cce49a416 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java
@@ -574,7 +574,7 @@ public Object[] getRowData() throws SQLServerException {
case Types.BIGINT: {
BigDecimal bd = new BigDecimal(data[pair.getKey() - 1].trim());
try {
- dataRow[pair.getKey() - 1] = bd.setScale(0, BigDecimal.ROUND_DOWN).longValueExact();
+ dataRow[pair.getKey() - 1] = bd.setScale(0, RoundingMode.DOWN).longValueExact();
} catch (ArithmeticException ex) {
String value = "'" + data[pair.getKey() - 1] + "'";
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue"));
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java
index a383e1946..50f711c29 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java
@@ -327,7 +327,7 @@ public void run() {
public SQLServerBulkCopy(Connection connection) throws SQLServerException {
loggerExternal.entering(loggerClassName, "SQLServerBulkCopy", connection);
- if (null == connection || !connection.getClass().equals(SQLServerConnection.class)) {
+ if (null == connection || !(connection instanceof SQLServerConnection)) {
SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDestConnection"), null, false);
}
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java
index 91a473b73..5f8bc8001 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java
@@ -22,10 +22,10 @@
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
+import java.util.Base64;
import java.util.Enumeration;
import java.util.Locale;
-import javax.xml.bind.DatatypeConverter;
/**
* The implementation of the key store provider for the Windows Certificate Store. This class enables using keys stored in the Windows Certificate
@@ -140,7 +140,7 @@ private String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmExcepti
byte[] der = cert.getEncoded();
md.update(der);
byte[] digest = md.digest();
- return DatatypeConverter.printHexBinary(digest);
+ return Base64.getEncoder().encodeToString(digest);
}
private CertificateDetails getCertificateByThumbprint(String storeLocation,
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
index a95ce9c9f..c8232d71a 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
@@ -1539,11 +1539,6 @@ Connection connectInternal(Properties propsIn,
throw new SQLServerException(SQLServerException.getErrString("R_AccessTokenWithUserPassword"), null);
}
- if ((!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows"))
- && (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()))) {
- throw new SQLServerException(SQLServerException.getErrString("R_AADIntegratedOnNonWindows"), null);
- }
-
// Turn off TNIR for FedAuth if user does not set TNIR explicitly
if (!userSetTNIR) {
if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) || (null != accessTokenInByte)) {
@@ -3186,8 +3181,9 @@ public PreparedStatement prepareStatement(String sql,
checkClosed();
PreparedStatement st;
-
- if (Util.use42Wrapper()) {
+
+ // Make sure SQLServerPreparedStatement42 is used for 4.2 and above.
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
}
@@ -3211,7 +3207,8 @@ private PreparedStatement prepareStatement(String sql,
PreparedStatement st;
- if (Util.use42Wrapper()) {
+ // Make sure SQLServerPreparedStatement42 is used for 4.2 and above.
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
st = new SQLServerPreparedStatement42(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting);
}
else {
@@ -3232,7 +3229,8 @@ public CallableStatement prepareCall(String sql,
CallableStatement st;
- if (Util.use42Wrapper()) {
+ // Make sure SQLServerCallableStatement42 is used for 4.2 and above.
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
st = new SQLServerCallableStatement42(this, sql, resultSetType, resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
}
@@ -3855,18 +3853,13 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe
// fedAuthInfo should not be null.
assert null != fedAuthInfo;
- // No:of milliseconds to sleep for the inital back off.
- int sleepInterval = 100;
-
- // No:of attempts, for tracing purposes, if we underwent retries.
- int numberOfAttempts = 0;
-
String user = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString());
String password = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString());
+ // No:of milliseconds to sleep for the inital back off.
+ int sleepInterval = 100;
+
while (true) {
- numberOfAttempts++;
-
if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) {
fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString);
@@ -3874,69 +3867,80 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe
break;
}
else if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) {
- try {
- long expirationFileTime = 0;
- FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(fedAuthInfo.stsurl, fedAuthInfo.spn,
- clientConnectionId.toString(), ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, expirationFileTime);
+
+ // If operating system is windows and sqljdbc_auth is loaded then choose the DLL authentication.
+ if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows") && AuthenticationJNI.isDllLoaded()) {
+ try {
+ long expirationFileTime = 0;
+ FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(fedAuthInfo.stsurl, fedAuthInfo.spn,
+ clientConnectionId.toString(), ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, expirationFileTime);
- // AccessToken should not be null.
- assert null != dllInfo.accessTokenBytes;
+ // AccessToken should not be null.
+ assert null != dllInfo.accessTokenBytes;
- byte[] accessTokenFromDLL = dllInfo.accessTokenBytes;
+ byte[] accessTokenFromDLL = dllInfo.accessTokenBytes;
- String accessToken = new String(accessTokenFromDLL, UTF_16LE);
+ String accessToken = new String(accessTokenFromDLL, UTF_16LE);
- fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn);
+ fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn);
- // Break out of the retry loop in successful case.
- break;
- }
- catch (DLLException adalException) {
-
- // the sqljdbc_auth.dll return -1 for errorCategory, if unable to load the adalsql.dll
- int errorCategory = adalException.GetCategory();
- if (-1 == errorCategory) {
- MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnableLoadADALSqlDll"));
- Object[] msgArgs = {Integer.toHexString(adalException.GetState())};
- throw new SQLServerException(form.format(msgArgs), null);
+ // Break out of the retry loop in successful case.
+ break;
}
+ catch (DLLException adalException) {
+
+ // the sqljdbc_auth.dll return -1 for errorCategory, if unable to load the adalsql.dll
+ int errorCategory = adalException.GetCategory();
+ if (-1 == errorCategory) {
+ MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_UnableLoadADALSqlDll"));
+ Object[] msgArgs = {Integer.toHexString(adalException.GetState())};
+ throw new SQLServerException(form.format(msgArgs), null);
+ }
- int millisecondsRemaining = TimerRemaining(timerExpire);
- if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory || timerHasExpired(timerExpire)
- || (sleepInterval >= millisecondsRemaining)) {
+ int millisecondsRemaining = TimerRemaining(timerExpire);
+ if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory || timerHasExpired(timerExpire)
+ || (sleepInterval >= millisecondsRemaining)) {
- String errorStatus = Integer.toHexString(adalException.GetStatus());
+ String errorStatus = Integer.toHexString(adalException.GetStatus());
- if (connectionlogger.isLoggable(Level.FINER)) {
- connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken.AdalException category:" + errorCategory
- + " error: " + errorStatus);
- }
+ if (connectionlogger.isLoggable(Level.FINER)) {
+ connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken.AdalException category:" + errorCategory
+ + " error: " + errorStatus);
+ }
- MessageFormat form1 = new MessageFormat(SQLServerException.getErrString("R_ADALAuthenticationMiddleErrorMessage"));
- String errorCode = Integer.toHexString(adalException.GetStatus()).toUpperCase();
- Object[] msgArgs1 = {errorCode, adalException.GetState()};
- SQLServerException middleException = new SQLServerException(form1.format(msgArgs1), adalException);
+ MessageFormat form1 = new MessageFormat(SQLServerException.getErrString("R_ADALAuthenticationMiddleErrorMessage"));
+ String errorCode = Integer.toHexString(adalException.GetStatus()).toUpperCase();
+ Object[] msgArgs1 = {errorCode, adalException.GetState()};
+ SQLServerException middleException = new SQLServerException(form1.format(msgArgs1), adalException);
- MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution"));
- Object[] msgArgs = {user, authenticationString};
- throw new SQLServerException(form.format(msgArgs), null, 0, middleException);
- }
+ MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution"));
+ Object[] msgArgs = {user, authenticationString};
+ throw new SQLServerException(form.format(msgArgs), null, 0, middleException);
+ }
- if (connectionlogger.isLoggable(Level.FINER)) {
- connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken sleeping: " + sleepInterval + " milliseconds.");
- connectionlogger
- .fine(toString() + " SQLServerConnection.getFedAuthToken remaining: " + millisecondsRemaining + " milliseconds.");
- }
+ if (connectionlogger.isLoggable(Level.FINER)) {
+ connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken sleeping: " + sleepInterval + " milliseconds.");
+ connectionlogger
+ .fine(toString() + " SQLServerConnection.getFedAuthToken remaining: " + millisecondsRemaining + " milliseconds.");
+ }
- try {
- Thread.sleep(sleepInterval);
- }
- catch (InterruptedException e1) {
- // re-interrupt the current thread, in order to restore the thread's interrupt status.
- Thread.currentThread().interrupt();
+ try {
+ Thread.sleep(sleepInterval);
+ }
+ catch (InterruptedException e1) {
+ // re-interrupt the current thread, in order to restore the thread's interrupt status.
+ Thread.currentThread().interrupt();
+ }
+ sleepInterval = sleepInterval * 2;
}
- sleepInterval = sleepInterval * 2;
}
+ // else choose ADAL4J for integrated authentication. This option is supported for both windows and unix, so we don't need to check the
+ // OS version here.
+ else {
+ fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString);
+ }
+ // Break out of the retry loop in successful case.
+ break;
}
}
@@ -4655,7 +4659,8 @@ public PreparedStatement prepareStatement(java.lang.String sql,
PreparedStatement st;
- if (Util.use42Wrapper()) {
+ // Make sure SQLServerPreparedStatement42 is used for 4.2 and above.
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
st = new SQLServerPreparedStatement42(this, sql, nType, nConcur, stmtColEncSetting);
}
else {
@@ -4690,7 +4695,8 @@ public CallableStatement prepareCall(String sql,
CallableStatement st;
- if (Util.use42Wrapper()) {
+ // Make sure SQLServerCallableStatement42 is used for 4.2 and above
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
st = new SQLServerCallableStatement42(this, sql, nType, nConcur, stmtColEncSetiing);
}
else {
@@ -5248,6 +5254,16 @@ public T unwrap(Class iface) throws SQLException {
return t;
}
+ public void beginRequest() throws SQLFeatureNotSupportedException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLFeatureNotSupportedException("beginRequest not implemented");
+ }
+
+ public void endRequest() throws SQLFeatureNotSupportedException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLFeatureNotSupportedException("endRequest not implemented");
+ }
+
/**
* Replace JDBC syntax parameter markets '?' with SQL Server paramter markers @p1, @p2 etc...
*
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java
new file mode 100644
index 000000000..217bcfc72
--- /dev/null
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java
@@ -0,0 +1,36 @@
+package com.microsoft.sqlserver.jdbc;
+
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.ShardingKey;
+
+public class SQLServerConnection43 extends SQLServerConnection implements ISQLServerConnection43 {
+
+ SQLServerConnection43(String parentInfo) throws SQLServerException {
+ super(parentInfo);
+ }
+
+ public void setShardingKey(ShardingKey shardingKey) throws SQLServerException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented"));
+ }
+
+ public void setShardingKey(ShardingKey shardingKey,
+ ShardingKey superShardingKey) throws SQLServerException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")) ;
+ }
+
+ public boolean setShardingKeyIfValid(ShardingKey shardingKey,
+ int timeout) throws SQLServerException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented"));
+ }
+
+ public boolean setShardingKeyIfValid(ShardingKey shardingKey,
+ ShardingKey superShardingKey,
+ int timeout) throws SQLServerException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented"));
+ }
+
+}
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
index 854275214..ebe4fee35 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
@@ -864,7 +864,7 @@ private void setIntProperty(Properties props,
int propValue) {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.entering(getClassNameLogging(), "set" + propKey, propValue);
- props.setProperty(propKey, ((Integer)propValue).toString());
+ props.setProperty(propKey, Integer.valueOf(propValue).toString());
loggerExternal.exiting(getClassNameLogging(), "set" + propKey);
}
@@ -1003,7 +1003,13 @@ SQLServerConnection getConnectionInternal(String username,
// Create new connection and connect.
if (dsLogger.isLoggable(Level.FINER))
dsLogger.finer(toString() + " Begin create new connection.");
- SQLServerConnection result = new SQLServerConnection(toString());
+ SQLServerConnection result = null;
+ if (Util.use43Wrapper()) {
+ result = new SQLServerConnection43(toString());
+ }
+ else {
+ result = new SQLServerConnection(toString());
+ }
result.connect(mergedProps, pooledConnection);
if (dsLogger.isLoggable(Level.FINER))
dsLogger.finer(toString() + " End create new connection " + result.toString());
@@ -1105,6 +1111,7 @@ public T unwrap(Class iface) throws SQLException {
return t;
}
+
// Returns unique id for each DataSource instance.
private static int nextDataSourceID() {
return baseDataSourceID.incrementAndGet();
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java
index 761eb26ae..6518d7057 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java
@@ -8,6 +8,7 @@
package com.microsoft.sqlserver.jdbc;
+import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import javax.naming.Context;
@@ -59,7 +60,7 @@ public Object getObjectInstance(Object ref,
// Create class instance and initialize using reference.
Class> dataSourceClass = Class.forName(className);
- Object dataSourceClassInstance = dataSourceClass.newInstance();
+ Object dataSourceClassInstance = dataSourceClass.getDeclaredConstructor().newInstance();
// If this class we created does not cast to SQLServerDataSource, then caller
// passed in the wrong reference to our factory.
@@ -79,6 +80,18 @@ public Object getObjectInstance(Object ref,
catch (IllegalAccessException e) {
SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true);
}
+ catch (IllegalArgumentException e) {
+ SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true);
+ }
+ catch (InvocationTargetException e) {
+ SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true);
+ }
+ catch (NoSuchMethodException e) {
+ SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true);
+ }
+ catch (SecurityException e) {
+ SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_invalidDataSourceReference"), null, true);
+ }
// no chance of getting here but to keep the compiler happy
return null;
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java
index f76c26af1..92ab65877 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java
@@ -401,6 +401,13 @@ public boolean supportsRefCursors() throws SQLException {
return false;
}
+ public boolean supportsSharding() throws SQLException {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ checkClosed();
+
+ return false;
+ }
+
/* L0 */ public java.sql.ResultSet getCatalogs() throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
index 9ea0c7a5b..ae4e1fd09 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
@@ -614,7 +614,12 @@ static String getPropertyOnlyName(String name,
// Merge connectProperties (from URL) and supplied properties from user.
Properties connectProperties = parseAndMergeProperties(Url, suppliedProperties);
if (connectProperties != null) {
- result = new SQLServerConnection(toString());
+ if (Util.use43Wrapper()) {
+ result = new SQLServerConnection43(toString());
+ }
+ else {
+ result = new SQLServerConnection(toString());
+ }
result.connect(connectProperties, null);
}
loggerExternal.exiting(getClassNameLogging(), "connect", result);
@@ -634,7 +639,7 @@ private Properties parseAndMergeProperties(String Url,
// put the user properties into the connect properties
int nTimeout = DriverManager.getLoginTimeout();
if (nTimeout > 0) {
- connectProperties.put(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), ((Integer)nTimeout).toString());
+ connectProperties.put(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString(), Integer.valueOf(nTimeout).toString());
}
// Merge connectProperties (from URL) and supplied properties from user.
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java
index 0d078cc1a..1950daf62 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java
@@ -24,6 +24,10 @@ final class DriverJDBCVersion {
static final void checkSupportsJDBC42() {
}
+
+ static final void checkSupportsJDBC43() {
+ throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported"));
+ }
static final void throwBatchUpdateException(SQLServerException lastError,
long[] updateCounts) throws BatchUpdateException {
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java
similarity index 55%
rename from src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java
rename to src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java
index 09bb912bf..1f60cd53a 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java
@@ -8,24 +8,29 @@
package com.microsoft.sqlserver.jdbc;
+import java.sql.BatchUpdateException;
+
/**
- * Shims for JDBC 4.1 JAR.
+ * Shims for JDBC 4.3 JAR.
*
- * JDBC 4.1 public methods should always check the SQLServerJdbcVersion first to make sure that they are not operable in any earlier driver version.
+ * JDBC 4.3 public methods should always check the SQLServerJdbcVersion first to make sure that they are not operable in any earlier driver version.
* That is, they should throw an exception, be a no-op, or whatever.
*/
final class DriverJDBCVersion {
+ // The 4.3 driver is compliant to JDBC 4.3.
static final int major = 4;
- static final int minor = 1;
+ static final int minor = 3;
+ static final void checkSupportsJDBC43() {
+ }
+
static final void checkSupportsJDBC42() {
- throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported"));
}
- // Stub for the new overloaded method in BatchUpdateException in JDBC 4.2
static final void throwBatchUpdateException(SQLServerException lastError,
- long[] updateCounts) {
- throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported"));
+ long[] updateCounts) throws BatchUpdateException {
+ throw new BatchUpdateException(lastError.getMessage(), lastError.getSQLState(), lastError.getErrorCode(), updateCounts,
+ new Throwable(lastError.getMessage()));
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java
index 63e701505..dc21fbbdb 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java
@@ -339,7 +339,6 @@ protected Object[][] getContents() {
{"R_TVPEmptyMetadata", "There are not enough fields in the Structured type. Structured types must have at least one field."},
{"R_TVPInvalidValue", "The value provided for Table-Valued Parameter {0} is not valid. Only SQLServerDataTable, ResultSet and ISQLServerDataRecord objects are supported."},
{"R_TVPInvalidColumnValue", "Input data is not in correct format."},
- {"R_AADIntegratedOnNonWindows","ActiveDirectoryIntegrated is only supported on Windows operating systems."},
{"R_TVPSortOrdinalGreaterThanFieldCount", "The sort ordinal {0} on field {1} exceeds the total number of fields."},
{"R_TVPMissingSortOrderOrOrdinal", "The sort order and ordinal must either both be specified, or neither should be specified (SortOrder.Unspecified and -1). The values given were: order = {0}, ordinal = {1}."},
{"R_TVPDuplicateSortOrdinal", "The sort ordinal {0} was specified twice."},
@@ -390,7 +389,7 @@ protected Object[][] getContents() {
{"R_invalidStringValue", "SQL_VARIANT does not support string values of length greater than 8000."},
{"R_invalidValueForTVPWithSQLVariant", "Use of TVPs containing null sql_variant columns is not supported."},
{"R_invalidDataTypeSupportForSQLVariant", "Unexpected TDS type ' '{0}' ' in SQL_VARIANT."},
- {"R_sslProtocolPropertyDescription", "SSL protocol label from TLS, TLSv1, TLSv1.1 & TLSv1.2. The default is TLS."},
- {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1 & TLSv1.2 are supported."},
+ {"R_sslProtocolPropertyDescription", "SSL protocol label from TLS, TLSv1, TLSv1.1, and TLSv1.2. The default is TLS."},
+ {"R_invalidSSLProtocol", "SSL Protocol {0} label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported."},
};
}
\ No newline at end of file
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java
index 9b7933705..37e1ac033 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java
@@ -939,7 +939,6 @@ private void updateCurrentRow(int rowsToMove) {
* and so on.
*
* @return false when there are no more rows to read
- * @return true otherwise
*/
public boolean next() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "next");
@@ -1041,8 +1040,7 @@ public boolean wasNull() throws SQLServerException {
}
/**
- * @return true if the cursor is before the first row in this result set
- * @return false otherwise or if thie result set contains no rows.
+ * @return true if the cursor is before the first row in this result set, returns false otherwise or if the result set contains no rows.
*/
public boolean isBeforeFirst() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "isBeforeFirst");
@@ -1144,7 +1142,6 @@ public boolean isAfterLast() throws SQLServerException {
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
* @return true if the cursor is on the first row in this result set
- * @return false otherwise
*/
public boolean isFirst() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "isFirst");
@@ -1182,7 +1179,6 @@ public boolean isFirst() throws SQLServerException {
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
* @return true if the cursor is on the last row in this result set
- * @return false otherwise
*/
public boolean isLast() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "isLast");
@@ -1302,8 +1298,7 @@ private void moveAfterLast() throws SQLServerException {
* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
- * @return true if the cursor is on a valid row
- * @return false if there are no rows in this ResultSet object
+ * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object
*/
public boolean first() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "first");
@@ -1353,8 +1348,7 @@ private void moveFirst() throws SQLServerException {
* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
- * @return true if the cursor is on a valid row
- * @return false if there are no rows in this ResultSet object
+ * @return true if the cursor is on a valid row, otherwise returns false if there are no rows in this ResultSet object
*/
public boolean last() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "last");
@@ -1442,8 +1436,8 @@ public int getRow() throws SQLServerException {
* This method should be called only on ResultSet objects that are scrollable: TYPE_SCROLL_SENSITIVE, TYPE_SCROLL_INSENSITIVE,
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
- * @return true if the cursor is on a valid row in this result set
- * @return false if the cursor is before the first row or after the last row
+ * @return true if the cursor is on a valid row in this result set, otherwise returns false if the cursor is before the first row or after the
+ * last row
*/
public boolean absolute(int row) throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "absolute");
@@ -1759,7 +1753,6 @@ private int clientMoveAbsolute(int row) throws SQLServerException {
* TYPE_SS_SCROLL_STATIC, TYPE_SS_SCROLL_KEYSET, TYPE_SS_SCROLL_DYNAMIC.
*
* @return true if the cursor is on a valid row in this result set
- * @return false otherwise
*/
public boolean previous() throws SQLServerException {
loggerExternal.entering(getClassNameLogging(), "previous");
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java
index 7e8881d8b..3e99ab41f 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSQLXML.java
@@ -23,6 +23,8 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
@@ -48,8 +50,6 @@
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
/**
* SQLServerSQLXML represents an XML object and implements a java.sql.SQLXML.
*/
@@ -405,12 +405,14 @@ private DOMSource getDOMSource() throws SQLException {
private SAXSource getSAXSource() throws SQLException {
try {
InputSource src = new InputSource(contents);
- XMLReader reader = XMLReaderFactory.createXMLReader();
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser parser = factory.newSAXParser();
+ XMLReader reader = parser.getXMLReader();
SAXSource saxSource = new SAXSource(reader, src);
return saxSource;
}
- catch (SAXException e) {
+ catch (SAXException | ParserConfigurationException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToParseXML"));
Object[] msgArgs = {e.toString()};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java
index 9385fd165..696cfa6c5 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java
@@ -1569,7 +1569,8 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException {
// Not an error. Is it a result set?
else if (nextResult.isResultSet()) {
- if (Util.use42Wrapper()) {
+ // Make sure SQLServerResultSet42 is used for 4.2 and above
+ if (Util.use42Wrapper() || Util.use43Wrapper()) {
resultSet = new SQLServerResultSet42(this);
}
else {
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java
index 76b886786..04f9335ff 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java
@@ -18,7 +18,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
-import javax.xml.bind.DatatypeConverter;
+import java.util.Base64;;
class CacheClear implements Runnable {
@@ -98,7 +98,7 @@ SQLServerSymmetricKey getKey(EncryptionKeyInfo keyInfo,
String keyLookupValue;
keyLookupValuebuffer.append(":");
- keyLookupValuebuffer.append(DatatypeConverter.printBase64Binary((new String(keyInfo.encryptedKey, UTF_8)).getBytes()));
+ keyLookupValuebuffer.append(Base64.getEncoder().encodeToString((new String(keyInfo.encryptedKey, UTF_8)).getBytes()));
keyLookupValuebuffer.append(":");
keyLookupValuebuffer.append(keyInfo.keyStoreName);
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java
index c1f8d69fd..424fe52ce 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java
@@ -44,7 +44,13 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer("Creating an internal control connection for" + toString());
- physicalControlConnection = new SQLServerConnection(toString());
+ physicalControlConnection = null;
+ if (Util.use43Wrapper()) {
+ physicalControlConnection = new SQLServerConnection43(toString());
+ }
+ else {
+ physicalControlConnection = new SQLServerConnection(toString());
+ }
physicalControlConnection.connect(controlConnectionProperties, null);
if (xaLogger.isLoggable(Level.FINER))
xaLogger.finer("Created an internal control connection" + physicalControlConnection.toString() + " for " + toString()
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java
index 63861a49c..868db7102 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java
@@ -62,7 +62,7 @@ static sqlVariantProbBytes valueOf(int intValue) {
if (!(0 <= intValue && intValue < valuesTypes.length) || null == (tdsType = valuesTypes[intValue])) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unknownSSType"));
- Object[] msgArgs = {(Integer)intValue};
+ Object[] msgArgs = {Integer.valueOf(intValue)};
throw new IllegalArgumentException(form.format(msgArgs));
}
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
index 028c5efa0..c8842ae09 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java
@@ -1006,6 +1006,28 @@ static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connec
static boolean use42Wrapper() {
return use42Wrapper;
}
+
+ static final boolean use43Wrapper;
+
+ static {
+ boolean supportJDBC43 = true;
+ try {
+ DriverJDBCVersion.checkSupportsJDBC43();
+ }
+ catch (UnsupportedOperationException e) {
+ supportJDBC43 = false;
+ }
+
+ double jvmVersion = Double.parseDouble(Util.SYSTEM_SPEC_VERSION);
+
+ use43Wrapper = supportJDBC43 && (9 <= jvmVersion);
+ }
+
+ // if driver is for JDBC 43 and jvm version is 9 or higher, then always return as SQLServerConnection43,
+ // otherwise return SQLServerConnection
+ static boolean use43Wrapper() {
+ return use43Wrapper;
+ }
}
final class SQLIdentifier {
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java
index e0ea30378..584cfd681 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java
@@ -2208,7 +2208,7 @@ void execute(DTV dtv,
if (null != bigDecimalValue) {
Integer inScale = dtv.getScale();
if (null != inScale && inScale != bigDecimalValue.scale())
- bigDecimalValue = bigDecimalValue.setScale(inScale, BigDecimal.ROUND_DOWN);
+ bigDecimalValue = bigDecimalValue.setScale(inScale, RoundingMode.DOWN);
}
dtv.setValue(bigDecimalValue, JavaType.BIGDECIMAL);
diff --git a/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java b/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java
index a337f0d74..bf91979c1 100644
--- a/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java
+++ b/src/samples/alwaysencrypted/src/main/java/AlwaysEncrypted.java
@@ -14,7 +14,6 @@
import java.sql.SQLException;
import java.sql.Statement;
-import javax.xml.bind.DatatypeConverter;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
@@ -116,7 +115,7 @@ public static void main(String[] args) {
*/
String createCEKSQL = "CREATE COLUMN ENCRYPTION KEY " + columnEncryptionKey + " WITH VALUES ( " + " COLUMN_MASTER_KEY = "
+ columnMasterKeyName + " , ALGORITHM = '" + algorithm + "' , ENCRYPTED_VALUE = 0x"
- + DatatypeConverter.printHexBinary(encryptedCEK) + " ) ";
+ + bytesToHexString(encryptedCEK, encryptedCEK.length) + " ) ";
try (Statement cekStatement = sourceConnection.createStatement()) {
cekStatement.executeUpdate(createCEKSQL);
@@ -129,6 +128,27 @@ public static void main(String[] args) {
e.printStackTrace();
}
}
+
+ /**
+ *
+ * @param b
+ * byte value
+ * @param length
+ * length of the array
+ * @return
+ */
+ final static char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ private static String bytesToHexString(byte[] b,
+ int length) {
+ StringBuilder sb = new StringBuilder(length * 2);
+ for (int i = 0; i < length; i++) {
+ int hexVal = b[i] & 0xFF;
+ sb.append(hexChars[(hexVal & 0xF0) >> 4]);
+ sb.append(hexChars[(hexVal & 0x0F)]);
+ }
+ return sb.toString();
+ }
// To avoid storing the sourceConnection String in your code,
// you can retrieve it from a configuration file.
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
index 9ae2445c1..dd4d588df 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
@@ -21,7 +21,6 @@
import java.util.LinkedList;
import java.util.Properties;
-import javax.xml.bind.DatatypeConverter;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -712,7 +711,7 @@ private static void createCEK(SQLServerColumnEncryptionKeyStoreProvider storePro
String cekSql = null;
byte[] key = storeProvider.encryptColumnEncryptionKey(javaKeyAliases, "RSA_OAEP", valuesDefault);
cekSql = "CREATE COLUMN ENCRYPTION KEY " + cekName + " WITH VALUES " + "(COLUMN_MASTER_KEY = " + cmkName
- + ", ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x" + DatatypeConverter.printHexBinary(key) + ")" + ";";
+ + ", ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x" + Util.bytesToHexString(key, key.length) + ")" + ";";
stmt.execute(cekSql);
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java
new file mode 100644
index 000000000..6873bbffe
--- /dev/null
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java
@@ -0,0 +1,228 @@
+/*
+ * Microsoft JDBC Driver for SQL Server
+ *
+ * Copyright(c) Microsoft Corporation All rights reserved.
+ *
+ * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
+ */
+package com.microsoft.sqlserver.jdbc;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.JDBCType;
+import java.sql.SQLException;
+import java.sql.ShardingKey;
+import java.util.Enumeration;
+import java.util.stream.Stream;
+
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+import javax.sql.XAConnection;
+
+import org.junit.jupiter.api.Test;
+import org.junit.platform.runner.JUnitPlatform;
+import org.junit.runner.RunWith;
+import org.opentest4j.TestAbortedException;
+
+import com.microsoft.sqlserver.testframework.AbstractTest;
+import com.microsoft.sqlserver.testframework.util.Util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+/**
+ * Tests JDBC 4.3 APIs
+ *
+ */
+@RunWith(JUnitPlatform.class)
+public class JDBC43Test extends AbstractTest {
+ ShardingKey superShardingKey = null;
+ ShardingKey shardingKey = null;
+
+ /**
+ * Tests that we are throwing the unsupported exception for connectionBuilder()
+ * @throws SQLException
+ * @throws TestAbortedException
+ *
+ * @since 1.9
+ */
+ @Test
+ public void connectionBuilderTest() throws TestAbortedException, SQLException {
+ assumeTrue(Util.supportJDBC43(connection));
+ SQLServerDataSource ds = new SQLServerDataSource();
+ try {
+ superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ try {
+ shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ try {
+ Connection con = ds.createConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey).superShardingKey(superShardingKey)
+ .build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ }
+
+ /**
+ * Tests that we are throwing the unsupported exception for connectionBuilder()
+ * @throws SQLException
+ * @throws TestAbortedException
+ *
+ * @since 1.9
+ */
+ @Test
+ public void xaConnectionBuilderTest() throws TestAbortedException, SQLException {
+ assumeTrue(Util.supportJDBC43(connection));
+ SQLServerXADataSource ds = new SQLServerXADataSource();
+ try {
+ superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ try {
+ shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ try {
+ XAConnection con = ds.createXAConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey)
+ .superShardingKey(superShardingKey).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ }
+
+ /**
+ * Tests that we are throwing the unsupported exception for createPooledConnectionBuilder()
+ * @throws SQLException
+ * @throws TestAbortedException
+ * @since 1.9
+ */
+ @Test
+ public void connectionPoolDataSourceTest() throws TestAbortedException, SQLException {
+ assumeTrue(Util.supportJDBC43(connection));
+ ConnectionPoolDataSource ds = new SQLServerConnectionPoolDataSource();
+ try {
+ superShardingKey = ds.createShardingKeyBuilder().subkey("EASTERN_REGION", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ try {
+ shardingKey = ds.createShardingKeyBuilder().subkey("PITTSBURGH_BRANCH", JDBCType.VARCHAR).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ try {
+ PooledConnection con = ds.createPooledConnectionBuilder().user("rafa").password("tennis").shardingKey(shardingKey)
+ .superShardingKey(superShardingKey).build();
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ }
+
+ /**
+ * Tests that we are throwing the unsupported exception for setShardingKeyIfValid()
+ * @throws SQLException
+ * @throws TestAbortedException
+ * @since 1.9
+ */
+ @Test
+ public void setShardingKeyIfValidTest() throws TestAbortedException, SQLException {
+ assumeTrue(Util.supportJDBC43(connection));
+ SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString);
+ try {
+ connection43.setShardingKeyIfValid(shardingKey, 10);
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ try {
+ connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10);
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ }
+
+ /**
+ * Tests that we are throwing the unsupported exception for setShardingKey()
+ * @throws SQLException
+ * @throws TestAbortedException
+ * @since 1.9
+ */
+ @Test
+ public void setShardingKeyTest() throws TestAbortedException, SQLException {
+ assumeTrue(Util.supportJDBC43(connection));
+ SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString);
+ try {
+ connection43.setShardingKey(shardingKey);
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+ try {
+ connection43.setShardingKey(shardingKey, superShardingKey);
+ }
+ catch (SQLException e) {
+ assert (e.getMessage().contains("not implemented"));
+ }
+
+ }
+
+ /**
+ * Tests the stream drivers() methods in java.sql.DriverManager
+ *
+ * @since 1.9
+ * @throws ClassNotFoundException
+ */
+ @Test
+ public void driversTest() throws ClassNotFoundException {
+ Stream drivers = DriverManager.drivers();
+ Object[] driversArray = drivers.toArray();
+ assertEquals(driversArray[0].getClass(), Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"));
+ }
+
+ /**
+ * Tests deregister Driver
+ *
+ * @throws SQLException
+ * @throws ClassNotFoundException
+ */
+ @Test
+ public void deregisterDriverTest() throws SQLException, ClassNotFoundException {
+ Enumeration drivers = DriverManager.getDrivers();
+ Driver current = null;
+ while (drivers.hasMoreElements()) {
+ current = drivers.nextElement();
+ DriverManager.deregisterDriver(current);
+ }
+ Stream currentDrivers = DriverManager.drivers();
+ Object[] driversArray = currentDrivers.toArray();
+ assertEquals(0, driversArray.length);
+
+ DriverManager.registerDriver(current);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java
index 911120ebb..bbd0ca5d6 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java
@@ -12,13 +12,14 @@
import java.util.Arrays;
import java.util.Random;
-import javax.xml.bind.DatatypeConverter;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
+import com.microsoft.sqlserver.jdbc.SQLServerException;
import com.microsoft.sqlserver.testframework.AbstractTest;
+import com.microsoft.sqlserver.testframework.util.Util;
/**
* This test validates PR #342. In this PR, DatatypeConverter#parseHexBinary is replaced with type casting. This tests validates if the behavior
@@ -38,12 +39,13 @@ public class DriverVersionTest extends AbstractTest {
/**
* validates version byte array generated by the original method and type casting reminds the same.
+ * @throws SQLServerException
*/
@Test
- public void testConnectionDriver() {
+ public void testConnectionDriver() throws SQLServerException {
// the original way to create version byte array
String interfaceLibVersion = generateInterfaceLibVersion();
- byte originalVersionBytes[] = DatatypeConverter.parseHexBinary(interfaceLibVersion);
+ byte originalVersionBytes[] = Util.hexStringToByte(interfaceLibVersion);
String originalBytes = Arrays.toString(originalVersionBytes);
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java
index 5d06220b8..c1e7e3351 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java
@@ -66,11 +66,11 @@ public void testWithUnSupportedProtocols(String sslProtocol) throws Exception {
try {
String url = connectionString + ";sslProtocol=" + sslProtocol;
con = DriverManager.getConnection(url);
- assertFalse(true, "Any protocol other than TLSv1, TLSv1.1 & TLSv1.2 should throw Exception");
+ assertFalse(true, "Any protocol other than TLSv1, TLSv1.1, and TLSv1.2 should throw Exception");
}
catch (SQLServerException e) {
assertTrue(true, "Should throw exception");
- String errMsg = "SSL Protocol " + sslProtocol + " label is not valid. Only TLS, TLSv1, TLSv1.1 & TLSv1.2 are supported.";
+ String errMsg = "SSL Protocol " + sslProtocol + " label is not valid. Only TLS, TLSv1, TLSv1.1, and TLSv1.2 are supported.";
assertTrue(errMsg.equals(e.getMessage()), "Message should be from SQL Server resources : " + e.getMessage());
}
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java
index 66ee1111b..8b99699b1 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java
@@ -13,6 +13,8 @@
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLWarning;
+import java.util.Arrays;
+import java.util.List;
import java.util.Properties;
import org.junit.jupiter.api.Test;
@@ -40,9 +42,18 @@ public void testWarnings() throws SQLException {
}
conn.setClientInfo(info2);
warn = conn.getWarnings();
- for (int i = 4; i >= 0; i--) {
- assertTrue(warn.toString().contains(infoArray[i]), "Warnings not found!");
+ for (int i = 0; i < 5; i++) {
+ boolean found = false;
+ List list = Arrays.asList(infoArray);
+ for (String word : list) {
+ if (warn.toString().contains(word)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue(found, "warning : '" + warn.toString() + "' not found!");
warn = warn.getNextWarning();
+ found = false;
}
conn.clearWarnings();
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java
index 667e3fc94..c1c12a8d9 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java
@@ -40,8 +40,9 @@ public class BatchExecutionWithNullTest extends AbstractTest {
static ResultSet rs = null;
/**
- * Test with combination of setString and setNull which cause the "Violation of PRIMARY KEY constraint and internally
- * "Could not find prepared statement with handle X" error.
+ * Test with combination of setString and setNull which cause the "Violation of PRIMARY KEY constraint and internally "Could not find prepared
+ * statement with handle X" error.
+ *
* @throws SQLException
*/
@Test
@@ -118,7 +119,7 @@ public void testSetup() throws TestAbortedException, Exception {
@AfterAll
public static void terminateVariation() throws SQLException {
connection = DriverManager.getConnection(connectionString);
-
+
SQLServerStatement stmt = (SQLServerStatement) connection.createStatement();
Utils.dropTableIfExists("esimple", stmt);
diff --git a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java
index f1e5167c4..336c2d304 100644
--- a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java
+++ b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java
@@ -11,6 +11,7 @@
import com.microsoft.sqlserver.jdbc.SQLServerConnection;
import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData;
+import com.microsoft.sqlserver.jdbc.SQLServerException;
import com.microsoft.sqlserver.jdbc.SQLServerStatementColumnEncryptionSetting;
/**
@@ -289,4 +290,82 @@ public static boolean supportJDBC42(Connection con) throws SQLException {
SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData();
return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 2);
}
+
+ /**
+ * Utility function for checking if the system supports JDBC 4.3
+ *
+ * @param con
+ * @return
+ */
+ public static boolean supportJDBC43(Connection con) throws SQLException {
+ SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData();
+ return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 3);
+ }
+
+ /**
+ *
+ * @param b
+ * byte value
+ * @param length
+ * length of the array
+ * @return
+ */
+ final static char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ public static String bytesToHexString(byte[] b,
+ int length) {
+ StringBuilder sb = new StringBuilder(length * 2);
+ for (int i = 0; i < length; i++) {
+ int hexVal = b[i] & 0xFF;
+ sb.append(hexChars[(hexVal & 0xF0) >> 4]);
+ sb.append(hexChars[(hexVal & 0x0F)]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * conversion routine valid values 0-9 a-f A-F throws exception when failed to convert
+ *
+ * @param value
+ * charArray
+ * @return
+ * @throws SQLServerException
+ */
+ static byte CharToHex(char value) throws SQLServerException {
+ byte ret = 0;
+ if (value >= 'A' && value <= 'F') {
+ ret = (byte) (value - 'A' + 10);
+ }
+ else if (value >= 'a' && value <= 'f') {
+ ret = (byte) (value - 'a' + 10);
+ }
+ else if (value >= '0' && value <= '9') {
+ ret = (byte) (value - '0');
+ }
+ else {
+ throw new IllegalArgumentException("The string is not in a valid hex format. ");
+ }
+ return ret;
+ }
+
+ /**
+ * Converts a string to an array of bytes
+ *
+ * @param hexV
+ * a hexized string representation of bytes
+ * @return
+ * @throws SQLServerException
+ */
+ public static byte[] hexStringToByte(String hexV) throws SQLServerException {
+ int len = hexV.length();
+ char orig[] = hexV.toCharArray();
+ if ((len % 2) != 0) {
+ throw new IllegalArgumentException("The string is not in a valid hex format: " + hexV);
+ }
+ byte[] bin = new byte[len / 2];
+ for (int i = 0; i < len / 2; i++) {
+ bin[i] = (byte) ((CharToHex(orig[2 * i]) << 4) + CharToHex(orig[2 * i + 1]));
+ }
+ return bin;
+ }
}