From 40edfde90ccb9ba9763822feb070bc38237285dd Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Sun, 24 Sep 2017 20:14:52 -0700 Subject: [PATCH 01/39] removing java.xml.bind dependency --- .../SQLServerAeadAes256CbcHmac256Factory.java | 4 +- .../apache/org/commoncodec/BinaryDecoder.java | 38 + .../apache/org/commoncodec/BinaryEncoder.java | 38 + .../mssql/apache/org/commoncodec/Decoder.java | 48 ++ .../org/commoncodec/DecoderException.java | 87 ++ .../mssql/apache/org/commoncodec/Encoder.java | 44 + .../org/commoncodec/EncoderException.java | 89 ++ .../apache/org/commoncodec/binary/Base64.java | 786 ++++++++++++++++++ .../org/commoncodec/binary/BaseNCodec.java | 547 ++++++++++++ .../commoncodec/binary/CharSequenceUtils.java | 79 ++ .../org/commoncodec/binary/StringUtils.java | 420 ++++++++++ 11 files changed, 2178 insertions(+), 2 deletions(-) create mode 100644 src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/Decoder.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/DecoderException.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/Encoder.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/EncoderException.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/Base64.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java create mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java index 8f71ec6b6..fc4ddae7d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java @@ -12,8 +12,8 @@ import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; +import mssql.apache.org.commoncodec.binary.Base64; -import javax.xml.bind.DatatypeConverter; /** * Factory for SQLServerAeadAes256CbcHmac256Algorithm @@ -38,7 +38,7 @@ SQLServerEncryptionAlgorithm create(SQLServerSymmetricKey columnEncryptionKey, } StringBuilder factoryKeyBuilder = new StringBuilder(); - factoryKeyBuilder.append(DatatypeConverter.printBase64Binary(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); + factoryKeyBuilder.append(Base64.encodeBase64(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); factoryKeyBuilder.append(":"); factoryKeyBuilder.append(encryptionType); diff --git a/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java b/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java new file mode 100644 index 000000000..32c4d2857 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + +/** + * Defines common decoding methods for byte array decoders. + * + * @version $Id$ + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param source + * A byte array which has been encoded with the appropriate encoder + * @return a byte array that contains decoded content + * @throws DecoderException + * A decoder exception is thrown if a Decoder encounters a failure condition during the decode process. + */ + byte[] decode(byte[] source) throws DecoderException; +} + diff --git a/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java b/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java new file mode 100644 index 000000000..868986661 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + +/** + * Defines common encoding methods for byte array encoders. + * + * @version $Id$ + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data as a byte array. + * + * @param source + * Data to be encoded + * @return A byte array containing the encoded data + * @throws EncoderException + * thrown if the Encoder encounters a failure condition during the encoding process. + */ + byte[] encode(byte[] source) throws EncoderException; +} + diff --git a/src/main/java/mssql/apache/org/commoncodec/Decoder.java b/src/main/java/mssql/apache/org/commoncodec/Decoder.java new file mode 100644 index 000000000..977814d6b --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/Decoder.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + + +/** + * Provides the highest level of abstraction for Decoders. + *

+ * This is the sister interface of {@link Encoder}. All Decoders implement this common generic interface. + * Allows a user to pass a generic Object to any Decoder implementation in the codec package. + *

+ * One of the two interfaces at the center of the codec package. + * + * @version $Id$ + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will + * try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a + * {@link ClassCastException} occurs this decode method will throw a DecoderException. + * + * @param source + * the object to decode + * @return a 'decoded" object + * @throws DecoderException + * a decoder exception can be thrown for any number of reasons. Some good candidates are that the + * parameter passed to this method is null, a param cannot be cast to the appropriate type for a + * specific encoder. + */ + Object decode(Object source) throws DecoderException; +} + diff --git a/src/main/java/mssql/apache/org/commoncodec/DecoderException.java b/src/main/java/mssql/apache/org/commoncodec/DecoderException.java new file mode 100644 index 000000000..bfcb2f689 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/DecoderException.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + + +/** + * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} + * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. + * + * @version $Id$ + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(final Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/mssql/apache/org/commoncodec/Encoder.java b/src/main/java/mssql/apache/org/commoncodec/Encoder.java new file mode 100644 index 000000000..3cf558c37 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/Encoder.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + +/** + * Provides the highest level of abstraction for Encoders. + *

+ * This is the sister interface of {@link Decoder}. Every implementation of Encoder provides this + * common generic interface which allows a user to pass a generic Object to any Encoder implementation + * in the codec package. + * + * @version $Id$ + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content as an Object. The Objects here may just be + * byte[] or Strings depending on the implementation used. + * + * @param source + * An object to encode + * @return An "encoded" Object + * @throws EncoderException + * An encoder exception is thrown if the encoder experiences a failure condition during the encoding + * process. + */ + Object encode(Object source) throws EncoderException; +} + diff --git a/src/main/java/mssql/apache/org/commoncodec/EncoderException.java b/src/main/java/mssql/apache/org/commoncodec/EncoderException.java new file mode 100644 index 000000000..6efbbeb09 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/EncoderException.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mssql.apache.org.commoncodec; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an + * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, + * characters outside of the expected range. + * + * @version $Id$ + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(final Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java b/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java new file mode 100644 index 000000000..b46897a1f --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java @@ -0,0 +1,786 @@ +package mssql.apache.org.commoncodec.binary; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

+ * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

+ *

+ * The class can be parameterized in the following manner with various constructors: + *

+ * + *

+ * The URL-safe parameter is only applied to encode operations. Decoding seamlessly handles both modes. + *

+ *

+ * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only + * encode/decode character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, + * UTF-8, etc). + *

+ *

+ * This class is thread-safe. + *

+ * + * @see RFC 2045 + * @since 1.0 + * @version $Id$ + */ +public class Base64 extends BaseNCodec { + + /** + * BASE32 characters are 6 bits in length. + * They are formed by taking a block of 3 octets to form a 24-bit string, + * which is converted into 4 BASE64 characters. + */ + private static final int BITS_PER_ENCODED_BYTE = 6; + private static final int BYTES_PER_UNENCODED_BLOCK = 3; + private static final int BYTES_PER_ENCODED_BLOCK = 4; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + *

+ * N.B. The next major release may break compatibility and make this field private. + *

+ * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + + /** + * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" + * equivalents as specified in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] STANDARD_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / + * changed to - and _ to make the encoded Base64 results more URL-SAFE. + * This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + /** + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified + * in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * alphabet but fall within the bounds of the array are translated to -1. + * + * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both + * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] DECODE_TABLE = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - / + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z + }; + + /** + * Base64 uses 6-bit fields. + */ + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + /** + * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able + * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch + * between the two modes. + */ + private final byte[] encodeTable; + + // Only one decode table currently; keep for consistency with Base32 code + private final byte[] decodeTable = DECODE_TABLE; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. + * encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ */ + public Base64() { + this(0); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. + *

+ * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. + *

+ * + *

+ * When decoding all variants are supported. + *

+ * + * @param urlSafe + * if true, URL-safe encoding is used. In most cases this should be set to + * false. + * @since 1.4 + */ + public Base64(final boolean urlSafe) { + this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of + * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when + * decoding. + * @since 1.4 + */ + public Base64(final int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of + * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when + * decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @throws IllegalArgumentException + * Thrown when the provided lineSeparator included some base64 characters. + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator) { + this(lineLength, lineSeparator, false); + } + + /** + * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. + *

+ * When encoding the line length and line separator are given in the constructor, and the encoding table is + * STANDARD_ENCODE_TABLE. + *

+ *

+ * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. + *

+ *

+ * When decoding all variants are supported. + *

+ * + * @param lineLength + * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of + * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when + * decoding. + * @param lineSeparator + * Each line of encoded data will end with this sequence of bytes. + * @param urlSafe + * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode + * operations. Decoding seamlessly handles both modes. + * Note: no padding is added when using the URL-safe alphabet. + * @throws IllegalArgumentException + * The provided lineSeparator included some base64 characters. That's not going to work! + * @since 1.4 + */ + public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, + lineLength, + lineSeparator == null ? 0 : lineSeparator.length); + // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 + // @see test case Base64Test.testConstructors() + if (lineSeparator != null) { + if (containsAlphabetOrPad(lineSeparator)) { + final String sep = StringUtils.newStringUtf8(lineSeparator); + throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); + } + if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + } else { + this.encodeSize = BYTES_PER_ENCODED_BLOCK; + this.lineSeparator = null; + } + this.decodeSize = this.encodeSize - 1; + this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + } + + /** + * Returns our current encode mode. True if we're URL-SAFE, false otherwise. + * + * @return true if we're in URL-SAFE mode, false otherwise. + * @since 1.4 + */ + public boolean isUrlSafe() { + return this.encodeTable == URL_SAFE_ENCODE_TABLE; + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with + * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, to flush last + * remaining bytes (if not multiple of 3). + *

+ *

Note: no padding is added when encoding using the URL-safe alphabet.

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of binary data to base64 encode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + * @param context + * the context to be used + */ + @Override + void encode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + context.eof = true; + if (0 == context.modulus && lineLength == 0) { + return; // no leftovers to process and not using chunking + } + final byte[] buffer = ensureBufferSize(encodeSize, context); + final int savedPos = context.pos; + switch (context.modulus) { // 0-2 + case 0 : // nothing to do here + break; + case 1 : // 8 bits = 6 + 2 + // top 6 bits: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS]; + // remaining 2: + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + buffer[context.pos++] = pad; + } + break; + + case 2 : // 16 bits = 6 + 6 + 4 + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS]; + // URL-SAFE skips the padding to further reduce size. + if (encodeTable == STANDARD_ENCODE_TABLE) { + buffer[context.pos++] = pad; + } + break; + default: + throw new IllegalStateException("Impossible modulus "+context.modulus); + } + context.currentLinePos += context.pos - savedPos; // keep track of current line position + // if currentPos == 0 we are at the start of a line, so don't add CRLF + if (lineLength > 0 && context.currentLinePos > 0) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(encodeSize, context); + context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK; + int b = in[inPos++]; + if (b < 0) { + b += 256; + } + context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE + if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS]; + buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS]; + context.currentLinePos += BYTES_PER_ENCODED_BLOCK; + if (lineLength > 0 && lineLength <= context.currentLinePos) { + System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); + context.pos += lineSeparator.length; + context.currentLinePos = 0; + } + } + } + } + } + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once + * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" + * call is not necessary when decoding, but it doesn't hurt, either. + *

+ *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are + * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, + * garbage-out philosophy: it will not check the provided data for validity. + *

+ *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in + * byte[] array of ascii data to base64 decode. + * @param inPos + * Position to start reading data from. + * @param inAvail + * Amount of bytes available from input for encoding. + * @param context + * the context to be used + */ + @Override + void decode(final byte[] in, int inPos, final int inAvail, final Context context) { + if (context.eof) { + return; + } + if (inAvail < 0) { + context.eof = true; + } + for (int i = 0; i < inAvail; i++) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + final byte b = in[inPos++]; + if (b == pad) { + // We're done. + context.eof = true; + break; + } + if (b >= 0 && b < DECODE_TABLE.length) { + final int result = DECODE_TABLE[b]; + if (result >= 0) { + context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK; + context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; + if (context.modulus == 0) { + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); + } + } + } + } + + // Two forms of EOF as far as base64 decoder is concerned: actual + // EOF (-1) and first time '=' character is encountered in stream. + // This approach makes the '=' padding characters completely optional. + if (context.eof && context.modulus != 0) { + final byte[] buffer = ensureBufferSize(decodeSize, context); + + // We have some spare bits remaining + // Output all whole multiples of 8 bits and ignore the rest + switch (context.modulus) { +// case 0 : // impossible, as excluded above + case 1 : // 6 bits - ignore entirely + // TODO not currently tested; perhaps it is impossible? + break; + case 2 : // 12 bits = 8 + 4 + context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + case 3 : // 18 bits = 8 + 8 + 2 + context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits + buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); + buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); + break; + default: + throw new IllegalStateException("Impossible modulus "+context.modulus); + } + } + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. + */ + @Deprecated + public static boolean isArrayByteBase64(final byte[] arrayOctet) { + return isBase64(arrayOctet); + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the base 64 alphabet, false otherwise. + * @since 1.4 + */ + public static boolean isBase64(final byte octet) { + return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); + } + + /** + * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param base64 + * String to test + * @return true if all characters in the String are valid characters in the Base64 alphabet or if + * the String is empty; false, otherwise + * @since 1.5 + */ + public static boolean isBase64(final String base64) { + return isBase64(StringUtils.getBytesUtf8(base64)); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the + * method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; + * false, otherwise + * @since 1.5 + */ + public static boolean isBase64(final byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + */ + public static byte[] encodeBase64(final byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * NOTE: We changed the behaviour of this method from multi-line chunking (commons-codec-1.4) to + * single-line non-chunking (commons-codec-1.5). + * + * @param binaryData + * binary data to encode + * @return String containing Base64 characters. + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + */ + public static String encodeBase64String(final byte[] binaryData) { + return StringUtils.newStringUsAscii(encodeBase64(binaryData, false)); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * Note: no padding is added. + * @param binaryData + * binary data to encode + * @return byte[] containing Base64 characters in their UTF-8 representation. + * @since 1.4 + */ + public static byte[] encodeBase64URLSafe(final byte[] binaryData) { + return encodeBase64(binaryData, false, true); + } + + /** + * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The + * url-safe variation emits - and _ instead of + and / characters. + * Note: no padding is added. + * @param binaryData + * binary data to encode + * @return String containing Base64 characters + * @since 1.4 + */ + public static String encodeBase64URLSafeString(final byte[] binaryData) { + return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true)); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks + * + * @param binaryData + * binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(final byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { + return encodeBase64(binaryData, isChunked, false); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * Note: no padding is added when encoding using the URL-safe alphabet. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { + return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe + * if true this encoder will emit - and _ instead of the usual + and / characters. + * Note: no padding is added when encoding using the URL-safe alphabet. + * @param maxResultSize + * The maximum result size to accept. + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than maxResultSize + * @since 1.4 + */ + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, + final boolean urlSafe, final int maxResultSize) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + + // Create this so can use the super-class method + // Also ensures that the same roundings are performed by the ctor and the code + final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); + final long len = b64.getEncodedLength(binaryData); + if (len > maxResultSize) { + throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + + len + + ") than the specified maximum size of " + + maxResultSize); + } + + return b64.encode(binaryData); + } + + /** + * Decodes a Base64 String into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or normal mode. + *

+ * + * @param base64String + * String containing Base64 data + * @return Array containing decoded data. + * @since 1.4 + */ + public static byte[] decodeBase64(final String base64String) { + return new Base64().decode(base64String); + } + + /** + * Decodes Base64 data into octets. + *

+ * Note: this method seamlessly handles data encoded in URL-safe or normal mode. + *

+ * + * @param base64Data + * Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(final byte[] base64Data) { + return new Base64().decode(base64Data); + } + + // Implementation of the Encoder Interface + + // Implementation of integer encoding used for crypto + /** + * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature. + * + * @param pArray + * a byte array containing base64 character data + * @return A BigInteger + * @since 1.4 + */ + public static BigInteger decodeInteger(final byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature. + * + * @param bigInt + * a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException + * if null is passed in + * @since 1.4 + */ + public static byte[] encodeInteger(final BigInteger bigInt) { + if (bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger without sign bit. + * + * @param bigInt + * BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(final BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + final byte[] bigBytes = bigInt.toByteArray(); + + if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if ((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + final byte[] resizedBytes = new byte[bitlen / 8]; + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + return resizedBytes; + } + + /** + * Returns whether or not the octet is in the Base64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the Base64 alphabet false otherwise. + */ + @Override + protected boolean isInAlphabet(final byte octet) { + return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; + } + +} \ No newline at end of file diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java b/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java new file mode 100644 index 000000000..d2a2dc33a --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java @@ -0,0 +1,547 @@ +package mssql.apache.org.commoncodec.binary; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Arrays; + +import org.apache.commons.codec.BinaryDecoder; +import org.apache.commons.codec.BinaryEncoder; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.EncoderException; + +/** + * Abstract superclass for Base-N encoders and decoders. + * + *

+ * This class is thread-safe. + *

+ * + * @version $Id$ + */ +public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder { + + /** + * Holds thread context so classes can be thread-safe. + * + * This class is not itself thread-safe; each thread must allocate its own copy. + * + * @since 1.7 + */ + static class Context { + + /** + * Place holder for the bytes we're dealing with for our based logic. + * Bitwise operations store and extract the encoding or decoding from this variable. + */ + int ibitWorkArea; + + /** + * Place holder for the bytes we're dealing with for our based logic. + * Bitwise operations store and extract the encoding or decoding from this variable. + */ + long lbitWorkArea; + + /** + * Buffer for streaming. + */ + byte[] buffer; + + /** + * Position where next character should be written in the buffer. + */ + int pos; + + /** + * Position where next character should be read from the buffer. + */ + int readPos; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, + * and must be thrown away. + */ + boolean eof; + + /** + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use + * it to make sure each encoded line never goes beyond lineLength (if lineLength > 0). + */ + int currentLinePos; + + /** + * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This + * variable helps track that. + */ + int modulus; + + Context() { + } + + /** + * Returns a String useful for debugging (especially within a debugger.) + * + * @return a String useful for debugging. + */ + @SuppressWarnings("boxing") // OK to ignore boxing here + @Override + public String toString() { + return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " + + "modulus=%s, pos=%s, readPos=%s]", this.getClass().getSimpleName(), Arrays.toString(buffer), + currentLinePos, eof, ibitWorkArea, lbitWorkArea, modulus, pos, readPos); + } + } + + /** + * EOF + * + * @since 1.7 + */ + static final int EOF = -1; + + /** + * MIME chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + public static final int MIME_CHUNK_SIZE = 76; + + /** + * PEM chunk size per RFC 1421 section 4.3.2.4. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 1421 section 4.3.2.4 + */ + public static final int PEM_CHUNK_SIZE = 64; + + private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; + + /** + * Defines the default buffer size - currently {@value} + * - must be large enough for at least one encoded block+separator + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** Mask used to extract 8 bits, used in decoding bytes */ + protected static final int MASK_8BITS = 0xff; + + /** + * Byte used to pad output. + */ + protected static final byte PAD_DEFAULT = '='; // Allow static access to default + + /** + * @deprecated Use {@link #pad}. Will be removed in 2.0. + */ + @Deprecated + protected final byte PAD = PAD_DEFAULT; // instance variable just in case it needs to vary later + + protected final byte pad; // instance variable just in case it needs to vary later + + /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ + private final int unencodedBlockSize; + + /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ + private final int encodedBlockSize; + + /** + * Chunksize for encoding. Not used when decoding. + * A value of zero or less implies no chunking of the encoded data. + * Rounded down to nearest multiple of encodedBlockSize. + */ + protected final int lineLength; + + /** + * Size of chunk separator. Not used unless {@link #lineLength} > 0. + */ + private final int chunkSeparatorLength; + + /** + * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} + * If chunkSeparatorLength is zero, then chunking is disabled. + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length lineLength + * @param chunkSeparatorLength the chunk separator length, if relevant + */ + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, + final int lineLength, final int chunkSeparatorLength) { + this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT); + } + + /** + * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} + * If chunkSeparatorLength is zero, then chunking is disabled. + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length lineLength + * @param chunkSeparatorLength the chunk separator length, if relevant + * @param pad byte used as padding byte. + */ + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, + final int lineLength, final int chunkSeparatorLength, final byte pad) { + this.unencodedBlockSize = unencodedBlockSize; + this.encodedBlockSize = encodedBlockSize; + final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; + this.lineLength = useChunking ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; + this.chunkSeparatorLength = chunkSeparatorLength; + + this.pad = pad; + } + + /** + * Returns true if this object has buffered data for reading. + * + * @param context the context to be used + * @return true if there is data still available for reading. + */ + boolean hasData(final Context context) { // package protected for access from I/O streams + return context.buffer != null; + } + + /** + * Returns the amount of buffered data available for reading. + * + * @param context the context to be used + * @return The amount of buffered data available for reading. + */ + int available(final Context context) { // package protected for access from I/O streams + return context.buffer != null ? context.pos - context.readPos : 0; + } + + /** + * Get the default buffer size. Can be overridden. + * + * @return {@link #DEFAULT_BUFFER_SIZE} + */ + protected int getDefaultBufferSize() { + return DEFAULT_BUFFER_SIZE; + } + + /** + * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. + * @param context the context to be used + */ + private byte[] resizeBuffer(final Context context) { + if (context.buffer == null) { + context.buffer = new byte[getDefaultBufferSize()]; + context.pos = 0; + context.readPos = 0; + } else { + final byte[] b = new byte[context.buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; + System.arraycopy(context.buffer, 0, b, 0, context.buffer.length); + context.buffer = b; + } + return context.buffer; + } + + /** + * Ensure that the buffer has room for size bytes + * + * @param size minimum spare space required + * @param context the context to be used + * @return the buffer + */ + protected byte[] ensureBufferSize(final int size, final Context context){ + if ((context.buffer == null) || (context.buffer.length < context.pos + size)){ + return resizeBuffer(context); + } + return context.buffer; + } + + /** + * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail + * bytes. Returns how many bytes were actually extracted. + *

+ * Package protected for access from I/O streams. + * + * @param b + * byte[] array to extract the buffered data into. + * @param bPos + * position in byte[] array to start extraction at. + * @param bAvail + * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @param context + * the context to be used + * @return The number of bytes successfully extracted into the provided byte[] array. + */ + int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) { + if (context.buffer != null) { + final int len = Math.min(available(context), bAvail); + System.arraycopy(context.buffer, context.readPos, b, bPos, len); + context.readPos += len; + if (context.readPos >= context.pos) { + context.buffer = null; // so hasData() will return false, and this method can return -1 + } + return len; + } + return context.eof ? EOF : 0; + } + + /** + * Checks if a byte value is whitespace or not. + * Whitespace is taken to mean: space, tab, CR, LF + * @param byteToCheck + * the byte to check + * @return true if byte is whitespace, false otherwise + */ + protected static boolean isWhiteSpace(final byte byteToCheck) { + switch (byteToCheck) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + return true; + default : + return false; + } + } + + /** + * Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of + * the Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. + * + * @param obj + * Object to encode + * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied. + * @throws EncoderException + * if the parameter supplied is not of type byte[] + */ + @Override + public Object encode(final Object obj) throws EncoderException { + if (!(obj instanceof byte[])) { + throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]"); + } + return encode((byte[]) obj); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet. + * Uses UTF8 encoding. + * + * @param pArray + * a byte array containing binary data + * @return A String containing only Base-N character data + */ + public String encodeToString(final byte[] pArray) { + return StringUtils.newStringUtf8(encode(pArray)); + } + + /** + * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. + * Uses UTF8 encoding. + * + * @param pArray a byte array containing binary data + * @return String containing only character data in the appropriate alphabet. + * @since 1.5 + * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring. + */ + public String encodeAsString(final byte[] pArray){ + return StringUtils.newStringUtf8(encode(pArray)); + } + + /** + * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of + * the Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. + * + * @param obj + * Object to decode + * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String + * supplied. + * @throws DecoderException + * if the parameter supplied is not of type byte[] + */ + @Override + public Object decode(final Object obj) throws DecoderException { + if (obj instanceof byte[]) { + return decode((byte[]) obj); + } else if (obj instanceof String) { + return decode((String) obj); + } else { + throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String"); + } + } + + /** + * Decodes a String containing characters in the Base-N alphabet. + * + * @param pArray + * A String containing Base-N character data + * @return a byte array containing binary data + */ + public byte[] decode(final String pArray) { + return decode(StringUtils.getBytesUtf8(pArray)); + } + + /** + * Decodes a byte[] containing characters in the Base-N alphabet. + * + * @param pArray + * A byte array containing Base-N character data + * @return a byte array containing binary data + */ + @Override + public byte[] decode(final byte[] pArray) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + final Context context = new Context(); + decode(pArray, 0, pArray.length, context); + decode(pArray, 0, EOF, context); // Notify decoder of EOF. + final byte[] result = new byte[context.pos]; + readResults(result, 0, result.length, context); + return result; + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A byte array containing only the base N alphabetic character data + */ + @Override + public byte[] encode(final byte[] pArray) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + return encode(pArray, 0, pArray.length); + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing + * characters in the alphabet. + * + * @param pArray + * a byte array containing binary data + * @param offset + * initial offset of the subarray. + * @param length + * length of the subarray. + * @return A byte array containing only the base N alphabetic character data + * @since 1.11 + */ + public byte[] encode(final byte[] pArray, int offset, int length) { + if (pArray == null || pArray.length == 0) { + return pArray; + } + final Context context = new Context(); + encode(pArray, offset, length, context); + encode(pArray, offset, EOF, context); // Notify encoder of EOF. + final byte[] buf = new byte[context.pos - context.readPos]; + readResults(buf, 0, buf.length, context); + return buf; + } + + // package protected for access from I/O streams + abstract void encode(byte[] pArray, int i, int length, Context context); + + // package protected for access from I/O streams + abstract void decode(byte[] pArray, int i, int length, Context context); + + /** + * Returns whether or not the octet is in the current alphabet. + * Does not allow whitespace or pad. + * + * @param value The value to test + * + * @return true if the value is defined in the current alphabet, false otherwise. + */ + protected abstract boolean isInAlphabet(byte value); + + /** + * Tests a given byte array to see if it contains only valid characters within the alphabet. + * The method optionally treats whitespace and pad as valid. + * + * @param arrayOctet byte array to test + * @param allowWSPad if true, then whitespace and PAD are also allowed + * + * @return true if all bytes are valid characters in the alphabet or if the byte array is empty; + * false, otherwise + */ + public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) { + for (byte octet : arrayOctet) { + if (!isInAlphabet(octet) && + (!allowWSPad || (octet != pad) && !isWhiteSpace(octet))) { + return false; + } + } + return true; + } + + /** + * Tests a given String to see if it contains only valid characters within the alphabet. + * The method treats whitespace and PAD as valid. + * + * @param basen String to test + * @return true if all characters in the String are valid characters in the alphabet or if + * the String is empty; false, otherwise + * @see #isInAlphabet(byte[], boolean) + */ + public boolean isInAlphabet(final String basen) { + return isInAlphabet(StringUtils.getBytesUtf8(basen), true); + } + + /** + * Tests a given byte array to see if it contains any characters within the alphabet or PAD. + * + * Intended for use in checking line-ending arrays + * + * @param arrayOctet + * byte array to test + * @return true if any byte is a valid character in the alphabet or PAD; false otherwise + */ + protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { + if (arrayOctet == null) { + return false; + } + for (final byte element : arrayOctet) { + if (pad == element || isInAlphabet(element)) { + return true; + } + } + return false; + } + + /** + * Calculates the amount of space needed to encode the supplied array. + * + * @param pArray byte[] array which will later be encoded + * + * @return amount of space needed to encoded the supplied array. + * Returns a long since a max-len array will require > Integer.MAX_VALUE + */ + public long getEncodedLength(final byte[] pArray) { + // Calculate non-chunked size - rounded up to allow for padding + // cast to long is needed to avoid possibility of overflow + long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize; + if (lineLength > 0) { // We're using chunking + // Round up to nearest multiple + len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength; + } + return len; + } +} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java b/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java new file mode 100644 index 000000000..3580b6166 --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mssql.apache.org.commoncodec.binary; + +/** + *

+ * Operations on {@link CharSequence} that are null safe. + *

+ *

+ * Copied from Apache Commons Lang r1586295 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see CharSequence + * @since 1.10 + */ +public class CharSequenceUtils { + + /** + * Green implementation of regionMatches. + * + * @param cs + * the CharSequence to be processed + * @param ignoreCase + * whether or not to be case insensitive + * @param thisStart + * the index to start on the cs CharSequence + * @param substring + * the CharSequence to be looked for + * @param start + * the index to start on the substring CharSequence + * @param length + * character length of the region + * @return whether the region matched + */ + static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, + final CharSequence substring, final int start, final int length) { + if (cs instanceof String && substring instanceof String) { + return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); + } + int index1 = thisStart; + int index2 = start; + int tmpLen = length; + + while (tmpLen-- > 0) { + final char c1 = cs.charAt(index1++); + final char c2 = substring.charAt(index2++); + + if (c1 == c2) { + continue; + } + + if (!ignoreCase) { + return false; + } + + // The same check as in String.regionMatches(): + if (Character.toUpperCase(c1) != Character.toUpperCase(c2) && + Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + return false; + } + } + + return true; + } +} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java b/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java new file mode 100644 index 000000000..3e2cd44fc --- /dev/null +++ b/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java @@ -0,0 +1,420 @@ +package mssql.apache.org.commoncodec.binary; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.codec.Charsets; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are + * specified in + * Standard charsets. + * + *

This class is immutable and thread-safe.

+ * + * @see CharEncoding + * @see Standard charsets + * @version $Id$ + * @since 1.4 + */ +public class StringUtils { + + /** + *

+ * Compares two CharSequences, returning true if they represent equal sequences of characters. + *

+ * + *

+ * nulls are handled without exceptions. Two null references are considered to be equal. + * The comparison is case sensitive. + *

+ * + *
+     * StringUtils.equals(null, null)   = true
+     * StringUtils.equals(null, "abc")  = false
+     * StringUtils.equals("abc", null)  = false
+     * StringUtils.equals("abc", "abc") = true
+     * StringUtils.equals("abc", "ABC") = false
+     * 
+ * + *

+ * Copied from Apache Commons Lang r1583482 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see Object#equals(Object) + * @param cs1 + * the first CharSequence, may be null + * @param cs2 + * the second CharSequence, may be null + * @return true if the CharSequences are equal (case-sensitive), or both null + * @since 1.10 + */ + public static boolean equals(final CharSequence cs1, final CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String) { + return cs1.equals(cs2); + } + return cs1.length() == cs2.length() && CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, cs1.length()); + } + + /** + * Calls {@link String#getBytes(Charset)} + * + * @param string + * The string to encode (if null, return null). + * @param charset + * The {@link Charset} to encode the String + * @return the encoded bytes + */ + private static byte[] getBytes(final String string, final Charset charset) { + if (string == null) { + return null; + } + return string.getBytes(charset); + } + + /** + * Calls {@link String#getBytes(Charset)} + * + * @param string + * The string to encode (if null, return null). + * @param charset + * The {@link Charset} to encode the String + * @return the encoded bytes + */ + private static ByteBuffer getByteBuffer(final String string, final Charset charset) { + if (string == null) { + return null; + } + return ByteBuffer.wrap(string.getBytes(charset)); + } + + /** + * Encodes the given string into a byte buffer using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + * @since 1.11 + */ + public static ByteBuffer getByteBufferUtf8(final String string) { + return getByteBuffer(string, Charsets.UTF_8); + } + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(final String string) { + return getBytes(string, Charsets.ISO_8859_1); + } + + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

+ * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param string + * the String to encode, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked(final String string, final String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(final String string) { + return getBytes(string, Charsets.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(final String string) { + return getBytes(string, Charsets.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(final String string) { + return getBytes(string, Charsets.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(final String string) { + return getBytes(string, Charsets.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(final String string) { + return getBytes(string, Charsets.UTF_8); + } + + private static IllegalStateException newIllegalStateException(final String charsetName, + final UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + * + * @param bytes + * The bytes to be decoded into characters + * @param charset + * The {@link Charset} to encode the String; not {@code null} + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if charset is {@code null} + */ + private static String newString(final byte[] bytes, final Charset charset) { + return bytes == null ? null : new String(bytes, charset); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + *

+ * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param bytes + * The bytes to be decoded into characters, may be null + * @param charsetName + * The name of a required {@link java.nio.charset.Charset} + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString(final byte[] bytes, final String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters, may be null + * @return A new String decoded from the specified array of bytes using the ISO-8859-1 charset, or + * null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringIso8859_1(final byte[] bytes) { + return newString(bytes, Charsets.ISO_8859_1); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the US-ASCII charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUsAscii(final byte[] bytes) { + return newString(bytes, Charsets.US_ASCII); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16 charset + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16(final byte[] bytes) { + return newString(bytes, Charsets.UTF_16); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16BE charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Be(final byte[] bytes) { + return newString(bytes, Charsets.UTF_16BE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16LE charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Le(final byte[] bytes) { + return newString(bytes, Charsets.UTF_16LE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-8 charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf8(final byte[] bytes) { + return newString(bytes, Charsets.UTF_8); + } + +} \ No newline at end of file From 380ab876e330a411d64b758db17218a73d43bece Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 12:31:18 -0700 Subject: [PATCH 02/39] removed dependency of java.xml.bind and updated pom file with latest javadoc pluggin. --- pom.xml | 2 +- .../microsoft/sqlserver/jdbc/IOBuffer.java | 10 +-- ...umnEncryptionCertificateStoreProvider.java | 3 +- .../jdbc/SQLServerSymmetricKeyCache.java | 4 +- .../jdbc/AlwaysEncrypted/AESetup.java | 3 +- .../jdbc/connection/DriverVersionTest.java | 8 ++- .../jdbc/connection/WarningTest.java | 15 +++- .../BatchExecutionWithNullTest.java | 9 ++- .../sqlserver/testframework/util/Util.java | 68 +++++++++++++++++++ 9 files changed, 100 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 160fdabd0..2ea8a91ff 100644 --- a/pom.xml +++ b/pom.xml @@ -294,7 +294,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.0.0-M1 true diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 4369d3c00..86041682b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -72,7 +72,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import javax.xml.bind.DatatypeConverter; final class TDS { // TDS protocol versions @@ -4956,7 +4955,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) { @@ -4975,7 +4974,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); } @@ -4989,7 +4988,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); } @@ -5026,9 +5025,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/SQLServerColumnEncryptionCertificateStoreProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java index 91a473b73..d4cddff78 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java @@ -25,7 +25,6 @@ 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 +139,7 @@ private String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmExcepti byte[] der = cert.getEncoded(); md.update(der); byte[] digest = md.digest(); - return DatatypeConverter.printHexBinary(digest); + return Util.bytesToHexString(digest, digest.length); } private CertificateDetails getCertificateByThumbprint(String storeLocation, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java index 76b886786..7dcb9f608 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 mssql.apache.org.commoncodec.binary.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.encodeBase64((new String(keyInfo.encryptedKey, UTF_8)).getBytes())); keyLookupValuebuffer.append(":"); keyLookupValuebuffer.append(keyInfo.keyStoreName); 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 6fc795a98..452e85bae 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; @@ -719,7 +718,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/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/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 ceb0bde9b..fde45dd8a 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 @@ -116,8 +117,10 @@ public void testSetup() throws TestAbortedException, Exception { } @AfterAll - public static void terminateVariation() throws SQLException { + public static void terminateVariation() throws TestAbortedException, Exception { + assumeTrue(13 <= new DBConnection(connectionString).getServerVersion(), + "Aborting test case as SQL Server version is not compatible with Always encrypted "); 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..e736b71f0 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,71 @@ public static boolean supportJDBC42(Connection con) throws SQLException { SQLServerDatabaseMetaData meta = (SQLServerDatabaseMetaData) con.getMetaData(); return (meta.getJDBCMajorVersion() >= 4 && meta.getJDBCMinorVersion() >= 2); } + + /** + * + * @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; + } } From 04e4a0b63d5f3fd6662fce42b256cb99d5c7f9f8 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 13:00:50 -0700 Subject: [PATCH 03/39] disabled appveyor and updated travic to use java 9 --- .travis.yml | 10 ++++++++-- appveyor.yml | 10 +++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c90e82b0..09ac66b6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ sudo: required language: java -jdk: - - oraclejdk8 +sudo: false +dist: trusty +jdk: oraclejdk9 +addons: + apt: + packages: + - oracle-java9-installer + services: - docker diff --git a/appveyor.yml b/appveyor.yml index 5d0ba1f95..5aa92d0b0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,9 +29,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 From 0b094dcb63b322f3a23cea52d5de402e61c2415d Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 13:13:33 -0700 Subject: [PATCH 04/39] Fixed NoSuchMethodError when compiling with Java 9 but running on java 8 and lower. --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 86041682b..d9aba3d53 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -72,6 +72,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import java.nio.Buffer; final class TDS { // TDS protocol versions @@ -3110,7 +3111,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 @@ -3186,8 +3187,8 @@ void startMessage(TDSCommand command, currentPacketSize = negotiatedPacketSize; } - socketBuffer.position(socketBuffer.limit()); - stagingBuffer.clear(); + ((Buffer) socketBuffer).position(((Buffer) socketBuffer).limit()); + ((Buffer)stagingBuffer).clear(); preparePacket(); writeMessageHeader(); @@ -3229,7 +3230,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 { @@ -3256,7 +3257,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 { @@ -3272,7 +3273,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 { @@ -3288,7 +3289,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 { @@ -3320,7 +3321,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 { @@ -3698,7 +3699,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 { @@ -3741,7 +3742,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; @@ -3768,7 +3769,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); } } @@ -3781,7 +3782,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); } } @@ -4103,7 +4104,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 @@ -4131,13 +4132,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; @@ -4149,14 +4150,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 @@ -4164,8 +4165,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()); } } @@ -4628,7 +4629,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; @@ -4654,9 +4655,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(); From 022d74784d15a77596156c4113066b97a1fb97ee Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 13:27:26 -0700 Subject: [PATCH 05/39] add support for JDBC 4.3 APIs and throw unsupported exceptions for those not implemented yet. Also updated the jdbc file to include building jre8 jar. --- pom.xml | 36 ++++ .../sqlserver/jdbc/SQLServerConnection.java | 35 ++++ .../sqlserver/jdbc/SQLServerDataSource.java | 12 ++ .../jdbc/SQLServerDatabaseMetaData.java | 7 + .../sqlserver/jdbc/SQLServerJdbc42.java | 4 + .../sqlserver/jdbc/SQLServerJdbc43.java | 36 ++++ .../microsoft/sqlserver/jdbc/JDBC43Test.java | 177 ++++++++++++++++++ .../sqlserver/testframework/util/Util.java | 11 ++ 8 files changed, 318 insertions(+) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java diff --git a/pom.xml b/pom.xml index 2ea8a91ff..d1fd61f22 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,7 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java 1.7 1.7 @@ -184,6 +185,7 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java 1.8 1.8 @@ -203,6 +205,40 @@ + + + build43 + + true + + + + + maven-compiler-plugin + 3.6.0 + + + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java + **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java + + 1.9 + 1.9 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + ${project.artifactId}-${project.version}.jre9-preview + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 730f13bf7..994d5b5ad 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -33,6 +33,7 @@ import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; +import java.sql.ShardingKey; import java.sql.Statement; import java.sql.Struct; import java.text.MessageFormat; @@ -5236,6 +5237,40 @@ 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"); + } + + public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public void setShardingKey(ShardingKey shardingKey, + ShardingKey superShardingKey) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + int timeout) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + ShardingKey superShardingKey, + int timeout) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder 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/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 49a6b25ba..9c4ebb2bb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -10,8 +10,10 @@ import java.io.PrintWriter; import java.sql.Connection; +import java.sql.ConnectionBuilder; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.sql.ShardingKeyBuilder; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; @@ -1105,6 +1107,16 @@ public T unwrap(Class iface) throws SQLException { return t; } + public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public ConnectionBuilder createConnectionBuilder() throws SQLException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented"); + } + // Returns unique id for each DataSource instance. private static int nextDataSourceID() { return baseDataSourceID.incrementAndGet(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 516a4d590..29406f03a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -397,6 +397,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/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/SQLServerJdbc43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java new file mode 100644 index 000000000..1f60cd53a --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java @@ -0,0 +1,36 @@ +/* + * 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.BatchUpdateException; + +/** + * Shims for JDBC 4.3 JAR. + * + * 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 = 3; + + static final void checkSupportsJDBC43() { + } + + static final void checkSupportsJDBC42() { + } + + static final void throwBatchUpdateException(SQLServerException lastError, + 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/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java new file mode 100644 index 000000000..b4ac96682 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -0,0 +1,177 @@ +/* + * 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.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 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/testframework/util/Util.java b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java index e736b71f0..336c2d304 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/util/Util.java @@ -291,6 +291,17 @@ public static boolean supportJDBC42(Connection con) throws SQLException { 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 From 98f9f6514ea6c8f0610aaeb2088a60ac67289711 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 13:30:19 -0700 Subject: [PATCH 06/39] fixed the javadoc warnings. --- .../sqlserver/jdbc/SQLServerResultSet.java | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index e937b27c3..cc75776f1 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"); @@ -1040,8 +1039,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"); @@ -1143,7 +1141,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"); @@ -1181,7 +1178,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"); @@ -1301,8 +1297,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"); @@ -1352,8 +1347,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"); @@ -1441,8 +1435,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"); From 7ed782d0483466207c1c36c0e3c41ed2d0801fb1 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 25 Sep 2017 13:35:11 -0700 Subject: [PATCH 07/39] update SQLServerJdbc41 to throw unsupportedError for 4.3 APIs --- .../java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java index 09bb912bf..270dbd3fe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java @@ -22,6 +22,10 @@ final class DriverJDBCVersion { static final void checkSupportsJDBC42() { throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); } + + static final void checkSupportsJDBC43() { + 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, From b42c20066ea7e7ff3ff8ce824fa7b12dd2071491 Mon Sep 17 00:00:00 2001 From: "v-xiangs@microsoft.com" Date: Thu, 28 Sep 2017 17:06:57 -0700 Subject: [PATCH 08/39] works for AAD integrated on driver side changes --- .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 124 ++++++++++++------ .../sqlserver/jdbc/SQLServerConnection.java | 67 +--------- 2 files changed, 92 insertions(+), 99 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index e3e10dd06..49ea2f856 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -1,6 +1,8 @@ package com.microsoft.sqlserver.jdbc; +import java.net.InetAddress; import java.net.MalformedURLException; +import java.net.UnknownHostException; import java.text.MessageFormat; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -15,41 +17,89 @@ class SQLServerADAL4JUtils { - static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, - String user, - String password, - String authenticationString) throws SQLServerException { - ExecutorService executorService = Executors.newFixedThreadPool(1); - try { - AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); - Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, user, - password, null); - - AuthenticationResult authenticationResult = future.get(); - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); - - return fedAuthToken; - } - catch (MalformedURLException | InterruptedException e) { - throw new SQLServerException(e.getMessage(), e); - } - catch (ExecutionException e) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = {user, authenticationString}; - - // 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(); - } - } + static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String user, String password, + String authenticationString) throws SQLServerException { + ExecutorService executorService = Executors.newFixedThreadPool(1); + try { + AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); + Future future = context.acquireToken(fedAuthInfo.spn, + ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, user, password, null); + + AuthenticationResult authenticationResult = future.get(); + SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), + authenticationResult.getExpiresOnDate()); + + return fedAuthToken; + } catch (MalformedURLException | InterruptedException e) { + throw new SQLServerException(e.getMessage(), e); + } catch (ExecutionException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = { user, authenticationString }; + + // 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(); + } + } + + static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String authenticationString) + throws SQLServerException { + ExecutorService executorService = Executors.newFixedThreadPool(1); + try { + System.out.println("fedAuthInfo.stsurl: " + fedAuthInfo.stsurl); + + // user name + String username = System.getenv("USERNAME"); + System.out.println("username: " + username); + + // fully qualified domain name for local host + String fullyQualifiedDomainName = InetAddress.getLocalHost().getCanonicalHostName(); + System.out.println("Hostname: " + fullyQualifiedDomainName); + + // username@fully_qualified_domain + String userDomainName = username + "@" + fullyQualifiedDomainName.substring(fullyQualifiedDomainName.indexOf(".") + 1); + System.out.println("userDomainName: " + userDomainName); + + AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); + Future future = context.acquireTokenIntegrated(userDomainName, fedAuthInfo.spn, + ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, null); + + AuthenticationResult authenticationResult = future.get(); + SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), + authenticationResult.getExpiresOnDate()); + + return fedAuthToken; + } catch (MalformedURLException | InterruptedException | UnknownHostException e) { + throw new SQLServerException(e.getMessage(), e); + } catch (ExecutionException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = { "testodbc", authenticationString }; + + // 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/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index aa301ccfa..0ada28872 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3859,71 +3859,14 @@ 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); - - // AccessToken should not be null. - assert null != dllInfo.accessTokenBytes; - - byte[] accessTokenFromDLL = dllInfo.accessTokenBytes; - - String accessToken = new String(accessTokenFromDLL, UTF_16LE); - - fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn); - - // Break out of the retry loop in successful case. - break; - } - catch (DLLException adalException) { + fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString); - // 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)) { - - String errorStatus = Integer.toHexString(adalException.GetStatus()); - - 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 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."); - } - - 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; - } + // Break out of the retry loop in successful case. + break; } } + + System.out.println("access token: " + fedAuthToken.accessToken); return fedAuthToken; } From d95575bd7d17e5f37cf4c3ea8f618c2d015bf6b9 Mon Sep 17 00:00:00 2001 From: "v-xiangs@microsoft.com" Date: Mon, 2 Oct 2017 17:40:20 -0700 Subject: [PATCH 09/39] remove hardcoded username from exception message --- .../com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index 49ea2f856..72ae7fd95 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -56,6 +56,9 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String use static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String authenticationString) throws SQLServerException { ExecutorService executorService = Executors.newFixedThreadPool(1); + + String userDomainName = null; + try { System.out.println("fedAuthInfo.stsurl: " + fedAuthInfo.stsurl); @@ -68,7 +71,7 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, System.out.println("Hostname: " + fullyQualifiedDomainName); // username@fully_qualified_domain - String userDomainName = username + "@" + fullyQualifiedDomainName.substring(fullyQualifiedDomainName.indexOf(".") + 1); + userDomainName = username + "@" + fullyQualifiedDomainName.substring(fullyQualifiedDomainName.indexOf(".") + 1); System.out.println("userDomainName: " + userDomainName); AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); @@ -84,7 +87,7 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = { "testodbc", authenticationString }; + Object[] msgArgs = {userDomainName, authenticationString }; // the cause error message uses \\n\\r which does not give correct format // change it to \r\n to provide correct format From 9d604ee4e989af343ad4dbf0fe2cca5d6fd24474 Mon Sep 17 00:00:00 2001 From: "v-xiangs@microsoft.com" Date: Tue, 24 Oct 2017 15:54:04 -0700 Subject: [PATCH 10/39] cleanup --- .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 82 +++++++++---------- .../sqlserver/jdbc/SQLServerConnection.java | 10 --- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index 72ae7fd95..a37925778 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -53,56 +53,54 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String use } } - static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String authenticationString) - throws SQLServerException { - ExecutorService executorService = Executors.newFixedThreadPool(1); - - String userDomainName = null; - - try { - System.out.println("fedAuthInfo.stsurl: " + fedAuthInfo.stsurl); + static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, + String authenticationString) throws SQLServerException { + ExecutorService executorService = Executors.newFixedThreadPool(1); + String userDomainName = null; + + try { // user name String username = System.getenv("USERNAME"); - System.out.println("username: " + username); // fully qualified domain name for local host String fullyQualifiedDomainName = InetAddress.getLocalHost().getCanonicalHostName(); - System.out.println("Hostname: " + fullyQualifiedDomainName); // username@fully_qualified_domain userDomainName = username + "@" + fullyQualifiedDomainName.substring(fullyQualifiedDomainName.indexOf(".") + 1); - System.out.println("userDomainName: " + userDomainName); - - AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); - Future future = context.acquireTokenIntegrated(userDomainName, fedAuthInfo.spn, - ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, null); - - AuthenticationResult authenticationResult = future.get(); - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), - authenticationResult.getExpiresOnDate()); - return fedAuthToken; - } catch (MalformedURLException | InterruptedException | UnknownHostException e) { - throw new SQLServerException(e.getMessage(), e); - } catch (ExecutionException e) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = {userDomainName, authenticationString }; - - // 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(); - } - } + // TODO: read userDomainName from Kerberos ticket + + AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); + Future future = context.acquireTokenIntegrated(userDomainName, fedAuthInfo.spn, + ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, null); + + AuthenticationResult authenticationResult = future.get(); + SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); + + return fedAuthToken; + } + catch (MalformedURLException | InterruptedException | UnknownHostException e) { + throw new SQLServerException(e.getMessage(), e); + } + catch (ExecutionException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = {userDomainName, authenticationString}; + + // 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/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 0ada28872..e14e70b4a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3840,18 +3840,10 @@ 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()); while (true) { - numberOfAttempts++; - if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString); @@ -3865,8 +3857,6 @@ else if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDi break; } } - - System.out.println("access token: " + fedAuthToken.accessToken); return fedAuthToken; } From c399a60d70c3fd3c6ee10e7d8e69aec3ee8a7c74 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 24 Oct 2017 16:52:54 -0700 Subject: [PATCH 11/39] remove error message when running AAD integrated on non-windows OS --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 5 ----- .../java/com/microsoft/sqlserver/jdbc/SQLServerResource.java | 1 - 2 files changed, 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index e14e70b4a..c71983bfe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1534,11 +1534,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)) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 63e701505..995e8b79f 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."}, From db5f09fc6eb08285fa7bb5e5d8f72764ff182b12 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Wed, 25 Oct 2017 18:21:19 -0700 Subject: [PATCH 12/39] read user name from kerberos TGT client name --- .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 106 ++++++++++-------- .../sqlserver/jdbc/SQLServerResource.java | 1 + 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index a37925778..46686a214 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -1,13 +1,13 @@ package com.microsoft.sqlserver.jdbc; -import java.net.InetAddress; +import java.io.IOException; import java.net.MalformedURLException; -import java.net.UnknownHostException; 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 com.microsoft.aad.adal4j.AuthenticationContext; import com.microsoft.aad.adal4j.AuthenticationException; @@ -15,64 +15,74 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection.ActiveDirectoryAuthentication; import com.microsoft.sqlserver.jdbc.SQLServerConnection.SqlFedAuthInfo; +import sun.security.krb5.Credentials; +import sun.security.krb5.KrbException; + class SQLServerADAL4JUtils { - static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String user, String password, - String authenticationString) throws SQLServerException { - ExecutorService executorService = Executors.newFixedThreadPool(1); - try { - AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); - Future future = context.acquireToken(fedAuthInfo.spn, - ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, user, password, null); - - AuthenticationResult authenticationResult = future.get(); - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), - authenticationResult.getExpiresOnDate()); - - return fedAuthToken; - } catch (MalformedURLException | InterruptedException e) { - throw new SQLServerException(e.getMessage(), e); - } catch (ExecutionException e) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = { user, authenticationString }; - - // 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(); - } - } + 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 { + ExecutorService executorService = Executors.newFixedThreadPool(1); + try { + AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); + Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, user, + password, null); + + AuthenticationResult authenticationResult = future.get(); + SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); + + return fedAuthToken; + } + catch (MalformedURLException | InterruptedException e) { + throw new SQLServerException(e.getMessage(), e); + } + catch (ExecutionException e) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); + Object[] msgArgs = {user, authenticationString}; + + // 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(); + } + } static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String authenticationString) throws SQLServerException { ExecutorService executorService = Executors.newFixedThreadPool(1); - String userDomainName = null; + String tgtClientName = null; try { - // user name - String username = System.getenv("USERNAME"); + Credentials cred = Credentials.acquireTGTFromCache(null, System.getenv("KRB5CCNAME")); - // fully qualified domain name for local host - String fullyQualifiedDomainName = InetAddress.getLocalHost().getCanonicalHostName(); + if (null == cred) { + throw new SQLServerException(SQLServerException.getErrString("R_AADIntegratedTGTNotFound"), null); + } - // username@fully_qualified_domain - userDomainName = username + "@" + fullyQualifiedDomainName.substring(fullyQualifiedDomainName.indexOf(".") + 1); + tgtClientName = cred.getClient().toString(); - // TODO: read userDomainName from Kerberos ticket + if (adal4jLogger.isLoggable(Level.FINE)) { + adal4jLogger.fine(adal4jLogger.toString() + " client name of Kerberos TGT is:" + tgtClientName); + } AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); - Future future = context.acquireTokenIntegrated(userDomainName, fedAuthInfo.spn, + Future future = context.acquireTokenIntegrated(tgtClientName, fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, null); AuthenticationResult authenticationResult = future.get(); @@ -80,12 +90,12 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, return fedAuthToken; } - catch (MalformedURLException | InterruptedException | UnknownHostException e) { + catch (InterruptedException | IOException | KrbException e) { throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = {userDomainName, authenticationString}; + Object[] msgArgs = {tgtClientName, authenticationString}; // the cause error message uses \\n\\r which does not give correct format // change it to \r\n to provide correct format diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 995e8b79f..f4a0896d3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -391,5 +391,6 @@ protected Object[][] getContents() { {"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_AADIntegratedTGTNotFound", "Kerberos ticket-granting ticket (TGT) cannot be found."}, }; } \ No newline at end of file From 1cc1170fb148391ca45a09417fd0b69d04f61790 Mon Sep 17 00:00:00 2001 From: "v-xiangs@microsoft.com" Date: Thu, 26 Oct 2017 12:18:49 -0700 Subject: [PATCH 13/39] add error message when Future's outcome has no AuthenticationResult but exception --- .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index 46686a214..fba887f10 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -69,6 +69,8 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String tgtClientName = null; try { + // Get Kerberos TGT ticket and retrieve client name. + // If KRB5CCNAME environment variable is not set, the method searches for default location Credentials cred = Credentials.acquireTGTFromCache(null, System.getenv("KRB5CCNAME")); if (null == cred) { @@ -97,17 +99,23 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); Object[] msgArgs = {tgtClientName, authenticationString}; - // 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); + 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(); From 06915980cf1d84640ebc45878b22d804927223c9 Mon Sep 17 00:00:00 2001 From: Nikhil Sidhaye Date: Fri, 3 Nov 2017 21:16:50 -0400 Subject: [PATCH 14/39] Version Update Configuration Rules. --- maven-version-rules.xml | 26 ++++++++++++++++++++++++++ pom.xml | 10 ++++++++++ 2 files changed, 36 insertions(+) create mode 100644 maven-version-rules.xml 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 a8ccbadda..a1c619590 100644 --- a/pom.xml +++ b/pom.xml @@ -336,6 +336,16 @@ + + + org.codehaus.mojo + versions-maven-plugin + true + + outdated-dependencies.txt + file:///${session.executionRootDirectory}/maven-version-rules.xml + + From 74e12a9d8bfa9b51668e39702af833d65c61d8db Mon Sep 17 00:00:00 2001 From: Nikhil Sidhaye Date: Sat, 4 Nov 2017 22:08:26 -0400 Subject: [PATCH 15/39] Added `outdated-dependencies.txt` in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index acb9b8d91..32c0d6071 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ local.properties .settings/ .gradle/ .loadpath +outdated-dependencies.txt # External tool builders .externalToolBuilders/ From 8004cdf0a58aedc28a58ad90170d3e7dd492a15e Mon Sep 17 00:00:00 2001 From: "v-xiangs@microsoft.com" Date: Fri, 24 Nov 2017 13:00:55 -0800 Subject: [PATCH 16/39] if password is null, it means AAD integrated auth --- .../com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index fba887f10..a2cf711a0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -84,8 +84,8 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, } AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); - Future future = context.acquireTokenIntegrated(tgtClientName, fedAuthInfo.spn, - ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, null); + Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, + tgtClientName, null, null); AuthenticationResult authenticationResult = future.get(); SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); From 1c389e830ea8d23bfefc27de6a1101ba89ea7737 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 27 Nov 2017 09:25:15 -0800 Subject: [PATCH 17/39] remove apache code for encoder and remove 4.1 compaitble jars --- pom.xml | 37 - .../SQLServerAeadAes256CbcHmac256Factory.java | 5 +- .../sqlserver/jdbc/SQLServerJdbc41.java | 35 - .../sqlserver/jdbc/SQLServerResultSet.java | 1 - .../jdbc/SQLServerSymmetricKeyCache.java | 4 +- .../apache/org/commoncodec/BinaryDecoder.java | 38 - .../apache/org/commoncodec/BinaryEncoder.java | 38 - .../mssql/apache/org/commoncodec/Decoder.java | 48 -- .../org/commoncodec/DecoderException.java | 87 -- .../mssql/apache/org/commoncodec/Encoder.java | 44 - .../org/commoncodec/EncoderException.java | 89 -- .../apache/org/commoncodec/binary/Base64.java | 786 ------------------ .../org/commoncodec/binary/BaseNCodec.java | 547 ------------ .../commoncodec/binary/CharSequenceUtils.java | 79 -- .../org/commoncodec/binary/StringUtils.java | 420 ---------- 15 files changed, 4 insertions(+), 2254 deletions(-) delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/Decoder.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/DecoderException.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/Encoder.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/EncoderException.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/Base64.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java delete mode 100644 src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java diff --git a/pom.xml b/pom.xml index d1fd61f22..5e0549e61 100644 --- a/pom.xml +++ b/pom.xml @@ -140,43 +140,8 @@ - - build41 - - - - maven-compiler-plugin - 3.6.0 - - - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - ${project.artifactId}-${project.version}.jre7-preview - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - - - build42 - - true - @@ -184,7 +149,6 @@ 3.6.0 - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java **/com/microsoft/sqlserver/jdbc/SQLServerJdbc43.java 1.8 @@ -218,7 +182,6 @@ 3.6.0 - **/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java 1.9 diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java index fc4ddae7d..1c3e1cd7e 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 mssql.apache.org.commoncodec.binary.Base64; - +import java.util.Base64; /** * Factory for SQLServerAeadAes256CbcHmac256Algorithm @@ -38,7 +37,7 @@ SQLServerEncryptionAlgorithm create(SQLServerSymmetricKey columnEncryptionKey, } StringBuilder factoryKeyBuilder = new StringBuilder(); - factoryKeyBuilder.append(Base64.encodeBase64(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); + factoryKeyBuilder.append(Base64.getEncoder().encode(new String(columnEncryptionKey.getRootKey(), UTF_8).getBytes())); factoryKeyBuilder.append(":"); factoryKeyBuilder.append(encryptionType); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java deleted file mode 100644 index 270dbd3fe..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerJdbc41.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; - -/** - * Shims for JDBC 4.1 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. - * That is, they should throw an exception, be a no-op, or whatever. - */ - -final class DriverJDBCVersion { - static final int major = 4; - static final int minor = 1; - - static final void checkSupportsJDBC42() { - throw new UnsupportedOperationException(SQLServerException.getErrString("R_notSupported")); - } - - static final void checkSupportsJDBC43() { - 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")); - } -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index cc75776f1..630ae1948 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -1752,7 +1752,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/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java index 7dcb9f608..819293f7b 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 mssql.apache.org.commoncodec.binary.Base64; +import java.util.Base64;; class CacheClear implements Runnable { @@ -98,7 +98,7 @@ SQLServerSymmetricKey getKey(EncryptionKeyInfo keyInfo, String keyLookupValue; keyLookupValuebuffer.append(":"); - keyLookupValuebuffer.append(Base64.encodeBase64((new String(keyInfo.encryptedKey, UTF_8)).getBytes())); + keyLookupValuebuffer.append(Base64.getEncoder().encode((new String(keyInfo.encryptedKey, UTF_8)).getBytes())); keyLookupValuebuffer.append(":"); keyLookupValuebuffer.append(keyInfo.keyStoreName); diff --git a/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java b/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java deleted file mode 100644 index 32c4d2857..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/BinaryDecoder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - -/** - * Defines common decoding methods for byte array decoders. - * - * @version $Id$ - */ -public interface BinaryDecoder extends Decoder { - - /** - * Decodes a byte array and returns the results as a byte array. - * - * @param source - * A byte array which has been encoded with the appropriate encoder - * @return a byte array that contains decoded content - * @throws DecoderException - * A decoder exception is thrown if a Decoder encounters a failure condition during the decode process. - */ - byte[] decode(byte[] source) throws DecoderException; -} - diff --git a/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java b/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java deleted file mode 100644 index 868986661..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/BinaryEncoder.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - -/** - * Defines common encoding methods for byte array encoders. - * - * @version $Id$ - */ -public interface BinaryEncoder extends Encoder { - - /** - * Encodes a byte array and return the encoded data as a byte array. - * - * @param source - * Data to be encoded - * @return A byte array containing the encoded data - * @throws EncoderException - * thrown if the Encoder encounters a failure condition during the encoding process. - */ - byte[] encode(byte[] source) throws EncoderException; -} - diff --git a/src/main/java/mssql/apache/org/commoncodec/Decoder.java b/src/main/java/mssql/apache/org/commoncodec/Decoder.java deleted file mode 100644 index 977814d6b..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/Decoder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - - -/** - * Provides the highest level of abstraction for Decoders. - *

- * This is the sister interface of {@link Encoder}. All Decoders implement this common generic interface. - * Allows a user to pass a generic Object to any Decoder implementation in the codec package. - *

- * One of the two interfaces at the center of the codec package. - * - * @version $Id$ - */ -public interface Decoder { - - /** - * Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will - * try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a - * {@link ClassCastException} occurs this decode method will throw a DecoderException. - * - * @param source - * the object to decode - * @return a 'decoded" object - * @throws DecoderException - * a decoder exception can be thrown for any number of reasons. Some good candidates are that the - * parameter passed to this method is null, a param cannot be cast to the appropriate type for a - * specific encoder. - */ - Object decode(Object source) throws DecoderException; -} - diff --git a/src/main/java/mssql/apache/org/commoncodec/DecoderException.java b/src/main/java/mssql/apache/org/commoncodec/DecoderException.java deleted file mode 100644 index bfcb2f689..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/DecoderException.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - - -/** - * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} - * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. - * - * @version $Id$ - */ -public class DecoderException extends Exception { - - /** - * Declares the Serial Version Uid. - * - * @see Always Declare Serial Version Uid - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with null as its detail message. The cause is not initialized, and may - * subsequently be initialized by a call to {@link #initCause}. - * - * @since 1.4 - */ - public DecoderException() { - super(); - } - - /** - * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently - * be initialized by a call to {@link #initCause}. - * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - */ - public DecoderException(final String message) { - super(message); - } - - /** - * Constructs a new exception with the specified detail message and cause. - *

- * Note that the detail message associated with cause is not automatically incorporated into this - * exception's detail message. - * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public DecoderException(final String message, final Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new exception with the specified cause and a detail message of (cause==null ? - * null : cause.toString()) (which typically contains the class and detail message of cause). - * This constructor is useful for exceptions that are little more than wrappers for other throwables. - * - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public DecoderException(final Throwable cause) { - super(cause); - } -} diff --git a/src/main/java/mssql/apache/org/commoncodec/Encoder.java b/src/main/java/mssql/apache/org/commoncodec/Encoder.java deleted file mode 100644 index 3cf558c37..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/Encoder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - -/** - * Provides the highest level of abstraction for Encoders. - *

- * This is the sister interface of {@link Decoder}. Every implementation of Encoder provides this - * common generic interface which allows a user to pass a generic Object to any Encoder implementation - * in the codec package. - * - * @version $Id$ - */ -public interface Encoder { - - /** - * Encodes an "Object" and returns the encoded content as an Object. The Objects here may just be - * byte[] or Strings depending on the implementation used. - * - * @param source - * An object to encode - * @return An "encoded" Object - * @throws EncoderException - * An encoder exception is thrown if the encoder experiences a failure condition during the encoding - * process. - */ - Object encode(Object source) throws EncoderException; -} - diff --git a/src/main/java/mssql/apache/org/commoncodec/EncoderException.java b/src/main/java/mssql/apache/org/commoncodec/EncoderException.java deleted file mode 100644 index 6efbbeb09..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/EncoderException.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mssql.apache.org.commoncodec; - -/** - * Thrown when there is a failure condition during the encoding process. This exception is thrown when an - * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, - * characters outside of the expected range. - * - * @version $Id$ - */ -public class EncoderException extends Exception { - - /** - * Declares the Serial Version Uid. - * - * @see Always Declare Serial Version Uid - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with null as its detail message. The cause is not initialized, and may - * subsequently be initialized by a call to {@link #initCause}. - * - * @since 1.4 - */ - public EncoderException() { - super(); - } - - /** - * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently - * be initialized by a call to {@link #initCause}. - * - * @param message - * a useful message relating to the encoder specific error. - */ - public EncoderException(final String message) { - super(message); - } - - /** - * Constructs a new exception with the specified detail message and cause. - * - *

- * Note that the detail message associated with cause is not automatically incorporated into this - * exception's detail message. - *

- * - * @param message - * The detail message which is saved for later retrieval by the {@link #getMessage()} method. - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public EncoderException(final String message, final Throwable cause) { - super(message, cause); - } - - /** - * Constructs a new exception with the specified cause and a detail message of (cause==null ? - * null : cause.toString()) (which typically contains the class and detail message of cause). - * This constructor is useful for exceptions that are little more than wrappers for other throwables. - * - * @param cause - * The cause which is saved for later retrieval by the {@link #getCause()} method. A null - * value is permitted, and indicates that the cause is nonexistent or unknown. - * @since 1.4 - */ - public EncoderException(final Throwable cause) { - super(cause); - } -} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java b/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java deleted file mode 100644 index b46897a1f..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/binary/Base64.java +++ /dev/null @@ -1,786 +0,0 @@ -package mssql.apache.org.commoncodec.binary; - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import java.math.BigInteger; - -/** - * Provides Base64 encoding and decoding as defined by RFC 2045. - * - *

- * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose - * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. - *

- *

- * The class can be parameterized in the following manner with various constructors: - *

- *
    - *
  • URL-safe mode: Default off.
  • - *
  • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of - * 4 in the encoded data. - *
  • Line separator: Default is CRLF ("\r\n")
  • - *
- *

- * The URL-safe parameter is only applied to encode operations. Decoding seamlessly handles both modes. - *

- *

- * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only - * encode/decode character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, - * UTF-8, etc). - *

- *

- * This class is thread-safe. - *

- * - * @see RFC 2045 - * @since 1.0 - * @version $Id$ - */ -public class Base64 extends BaseNCodec { - - /** - * BASE32 characters are 6 bits in length. - * They are formed by taking a block of 3 octets to form a 24-bit string, - * which is converted into 4 BASE64 characters. - */ - private static final int BITS_PER_ENCODED_BYTE = 6; - private static final int BYTES_PER_UNENCODED_BLOCK = 3; - private static final int BYTES_PER_ENCODED_BLOCK = 4; - - /** - * Chunk separator per RFC 2045 section 2.1. - * - *

- * N.B. The next major release may break compatibility and make this field private. - *

- * - * @see RFC 2045 section 2.1 - */ - static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" - * equivalents as specified in Table 1 of RFC 2045. - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] STANDARD_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / - * changed to - and _ to make the encoded Base64 results more URL-SAFE. - * This table is only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - /** - * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified - * in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 - * alphabet but fall within the bounds of the array are translated to -1. - * - * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both - * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). - * - * Thanks to "commons" project in ws.apache.org for this code. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - */ - private static final byte[] DECODE_TABLE = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - / - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9 - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4f A-O - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z - }; - - /** - * Base64 uses 6-bit fields. - */ - /** Mask used to extract 6 bits, used when encoding */ - private static final int MASK_6BITS = 0x3f; - - // The static final fields above are used for the original static byte[] methods on Base64. - // The private member fields below are used with the new streaming approach, which requires - // some state be preserved between calls of encode() and decode(). - - /** - * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able - * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch - * between the two modes. - */ - private final byte[] encodeTable; - - // Only one decode table currently; keep for consistency with Base32 code - private final byte[] decodeTable = DECODE_TABLE; - - /** - * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. - */ - private final byte[] lineSeparator; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * decodeSize = 3 + lineSeparator.length; - */ - private final int decodeSize; - - /** - * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. - * encodeSize = 4 + lineSeparator.length; - */ - private final int encodeSize; - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- */ - public Base64() { - this(0); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode. - *

- * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. - *

- * - *

- * When decoding all variants are supported. - *

- * - * @param urlSafe - * if true, URL-safe encoding is used. In most cases this should be set to - * false. - * @since 1.4 - */ - public Base64(final boolean urlSafe) { - this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. - * @since 1.4 - */ - public Base64(final int lineLength) { - this(lineLength, CHUNK_SEPARATOR); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @throws IllegalArgumentException - * Thrown when the provided lineSeparator included some base64 characters. - * @since 1.4 - */ - public Base64(final int lineLength, final byte[] lineSeparator) { - this(lineLength, lineSeparator, false); - } - - /** - * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - *

- * When encoding the line length and line separator are given in the constructor, and the encoding table is - * STANDARD_ENCODE_TABLE. - *

- *

- * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - *

- *

- * When decoding all variants are supported. - *

- * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @param urlSafe - * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * Note: no padding is added when using the URL-safe alphabet. - * @throws IllegalArgumentException - * The provided lineSeparator included some base64 characters. That's not going to work! - * @since 1.4 - */ - public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { - super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, - lineLength, - lineSeparator == null ? 0 : lineSeparator.length); - // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 - // @see test case Base64Test.testConstructors() - if (lineSeparator != null) { - if (containsAlphabetOrPad(lineSeparator)) { - final String sep = StringUtils.newStringUtf8(lineSeparator); - throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); - } - if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE - this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; - this.lineSeparator = new byte[lineSeparator.length]; - System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - } else { - this.encodeSize = BYTES_PER_ENCODED_BLOCK; - this.lineSeparator = null; - } - this.decodeSize = this.encodeSize - 1; - this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; - } - - /** - * Returns our current encode mode. True if we're URL-SAFE, false otherwise. - * - * @return true if we're in URL-SAFE mode, false otherwise. - * @since 1.4 - */ - public boolean isUrlSafe() { - return this.encodeTable == URL_SAFE_ENCODE_TABLE; - } - - /** - *

- * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with - * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, to flush last - * remaining bytes (if not multiple of 3). - *

- *

Note: no padding is added when encoding using the URL-safe alphabet.

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of binary data to base64 encode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - * @param context - * the context to be used - */ - @Override - void encode(final byte[] in, int inPos, final int inAvail, final Context context) { - if (context.eof) { - return; - } - // inAvail < 0 is how we're informed of EOF in the underlying data we're - // encoding. - if (inAvail < 0) { - context.eof = true; - if (0 == context.modulus && lineLength == 0) { - return; // no leftovers to process and not using chunking - } - final byte[] buffer = ensureBufferSize(encodeSize, context); - final int savedPos = context.pos; - switch (context.modulus) { // 0-2 - case 0 : // nothing to do here - break; - case 1 : // 8 bits = 6 + 2 - // top 6 bits: - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS]; - // remaining 2: - buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[context.pos++] = pad; - buffer[context.pos++] = pad; - } - break; - - case 2 : // 16 bits = 6 + 6 + 4 - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS]; - // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { - buffer[context.pos++] = pad; - } - break; - default: - throw new IllegalStateException("Impossible modulus "+context.modulus); - } - context.currentLinePos += context.pos - savedPos; // keep track of current line position - // if currentPos == 0 we are at the start of a line, so don't add CRLF - if (lineLength > 0 && context.currentLinePos > 0) { - System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); - context.pos += lineSeparator.length; - } - } else { - for (int i = 0; i < inAvail; i++) { - final byte[] buffer = ensureBufferSize(encodeSize, context); - context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK; - int b = in[inPos++]; - if (b < 0) { - b += 256; - } - context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE - if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS]; - buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS]; - context.currentLinePos += BYTES_PER_ENCODED_BLOCK; - if (lineLength > 0 && lineLength <= context.currentLinePos) { - System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length); - context.pos += lineSeparator.length; - context.currentLinePos = 0; - } - } - } - } - } - - /** - *

- * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once - * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" - * call is not necessary when decoding, but it doesn't hurt, either. - *

- *

- * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are - * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, - * garbage-out philosophy: it will not check the provided data for validity. - *

- *

- * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. - * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ - *

- * - * @param in - * byte[] array of ascii data to base64 decode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - * @param context - * the context to be used - */ - @Override - void decode(final byte[] in, int inPos, final int inAvail, final Context context) { - if (context.eof) { - return; - } - if (inAvail < 0) { - context.eof = true; - } - for (int i = 0; i < inAvail; i++) { - final byte[] buffer = ensureBufferSize(decodeSize, context); - final byte b = in[inPos++]; - if (b == pad) { - // We're done. - context.eof = true; - break; - } - if (b >= 0 && b < DECODE_TABLE.length) { - final int result = DECODE_TABLE[b]; - if (result >= 0) { - context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK; - context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; - if (context.modulus == 0) { - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); - buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); - } - } - } - } - - // Two forms of EOF as far as base64 decoder is concerned: actual - // EOF (-1) and first time '=' character is encountered in stream. - // This approach makes the '=' padding characters completely optional. - if (context.eof && context.modulus != 0) { - final byte[] buffer = ensureBufferSize(decodeSize, context); - - // We have some spare bits remaining - // Output all whole multiples of 8 bits and ignore the rest - switch (context.modulus) { -// case 0 : // impossible, as excluded above - case 1 : // 6 bits - ignore entirely - // TODO not currently tested; perhaps it is impossible? - break; - case 2 : // 12 bits = 8 + 4 - context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits - buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); - break; - case 3 : // 18 bits = 8 + 8 + 2 - context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits - buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); - buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); - break; - default: - throw new IllegalStateException("Impossible modulus "+context.modulus); - } - } - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the - * method treats whitespace as valid. - * - * @param arrayOctet - * byte array to test - * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; - * false, otherwise - * @deprecated 1.5 Use {@link #isBase64(byte[])}, will be removed in 2.0. - */ - @Deprecated - public static boolean isArrayByteBase64(final byte[] arrayOctet) { - return isBase64(arrayOctet); - } - - /** - * Returns whether or not the octet is in the base 64 alphabet. - * - * @param octet - * The value to test - * @return true if the value is defined in the the base 64 alphabet, false otherwise. - * @since 1.4 - */ - public static boolean isBase64(final byte octet) { - return octet == PAD_DEFAULT || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1); - } - - /** - * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the - * method treats whitespace as valid. - * - * @param base64 - * String to test - * @return true if all characters in the String are valid characters in the Base64 alphabet or if - * the String is empty; false, otherwise - * @since 1.5 - */ - public static boolean isBase64(final String base64) { - return isBase64(StringUtils.getBytesUtf8(base64)); - } - - /** - * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the - * method treats whitespace as valid. - * - * @param arrayOctet - * byte array to test - * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; - * false, otherwise - * @since 1.5 - */ - public static boolean isBase64(final byte[] arrayOctet) { - for (int i = 0; i < arrayOctet.length; i++) { - if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { - return false; - } - } - return true; - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * @param binaryData - * binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - */ - public static byte[] encodeBase64(final byte[] binaryData) { - return encodeBase64(binaryData, false); - } - - /** - * Encodes binary data using the base64 algorithm but does not chunk the output. - * - * NOTE: We changed the behaviour of this method from multi-line chunking (commons-codec-1.4) to - * single-line non-chunking (commons-codec-1.5). - * - * @param binaryData - * binary data to encode - * @return String containing Base64 characters. - * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). - */ - public static String encodeBase64String(final byte[] binaryData) { - return StringUtils.newStringUsAscii(encodeBase64(binaryData, false)); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The - * url-safe variation emits - and _ instead of + and / characters. - * Note: no padding is added. - * @param binaryData - * binary data to encode - * @return byte[] containing Base64 characters in their UTF-8 representation. - * @since 1.4 - */ - public static byte[] encodeBase64URLSafe(final byte[] binaryData) { - return encodeBase64(binaryData, false, true); - } - - /** - * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The - * url-safe variation emits - and _ instead of + and / characters. - * Note: no padding is added. - * @param binaryData - * binary data to encode - * @return String containing Base64 characters - * @since 1.4 - */ - public static String encodeBase64URLSafeString(final byte[] binaryData) { - return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true)); - } - - /** - * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks - * - * @param binaryData - * binary data to encode - * @return Base64 characters chunked in 76 character blocks - */ - public static byte[] encodeBase64Chunked(final byte[] binaryData) { - return encodeBase64(binaryData, true); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { - return encodeBase64(binaryData, isChunked, false); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if true this encoder will emit - and _ instead of the usual + and / characters. - * Note: no padding is added when encoding using the URL-safe alphabet. - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} - * @since 1.4 - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { - return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE); - } - - /** - * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. - * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if true this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if true this encoder will emit - and _ instead of the usual + and / characters. - * Note: no padding is added when encoding using the URL-safe alphabet. - * @param maxResultSize - * The maximum result size to accept. - * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than maxResultSize - * @since 1.4 - */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, - final boolean urlSafe, final int maxResultSize) { - if (binaryData == null || binaryData.length == 0) { - return binaryData; - } - - // Create this so can use the super-class method - // Also ensures that the same roundings are performed by the ctor and the code - final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); - final long len = b64.getEncodedLength(binaryData); - if (len > maxResultSize) { - throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + - len + - ") than the specified maximum size of " + - maxResultSize); - } - - return b64.encode(binaryData); - } - - /** - * Decodes a Base64 String into octets. - *

- * Note: this method seamlessly handles data encoded in URL-safe or normal mode. - *

- * - * @param base64String - * String containing Base64 data - * @return Array containing decoded data. - * @since 1.4 - */ - public static byte[] decodeBase64(final String base64String) { - return new Base64().decode(base64String); - } - - /** - * Decodes Base64 data into octets. - *

- * Note: this method seamlessly handles data encoded in URL-safe or normal mode. - *

- * - * @param base64Data - * Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(final byte[] base64Data) { - return new Base64().decode(base64Data); - } - - // Implementation of the Encoder Interface - - // Implementation of integer encoding used for crypto - /** - * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature. - * - * @param pArray - * a byte array containing base64 character data - * @return A BigInteger - * @since 1.4 - */ - public static BigInteger decodeInteger(final byte[] pArray) { - return new BigInteger(1, decodeBase64(pArray)); - } - - /** - * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature. - * - * @param bigInt - * a BigInteger - * @return A byte array containing base64 character data - * @throws NullPointerException - * if null is passed in - * @since 1.4 - */ - public static byte[] encodeInteger(final BigInteger bigInt) { - if (bigInt == null) { - throw new NullPointerException("encodeInteger called with null parameter"); - } - return encodeBase64(toIntegerBytes(bigInt), false); - } - - /** - * Returns a byte-array representation of a BigInteger without sign bit. - * - * @param bigInt - * BigInteger to be converted - * @return a byte array representation of the BigInteger parameter - */ - static byte[] toIntegerBytes(final BigInteger bigInt) { - int bitlen = bigInt.bitLength(); - // round bitlen - bitlen = ((bitlen + 7) >> 3) << 3; - final byte[] bigBytes = bigInt.toByteArray(); - - if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { - return bigBytes; - } - // set up params for copying everything but sign bit - int startSrc = 0; - int len = bigBytes.length; - - // if bigInt is exactly byte-aligned, just skip signbit in copy - if ((bigInt.bitLength() % 8) == 0) { - startSrc = 1; - len--; - } - final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec - final byte[] resizedBytes = new byte[bitlen / 8]; - System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); - return resizedBytes; - } - - /** - * Returns whether or not the octet is in the Base64 alphabet. - * - * @param octet - * The value to test - * @return true if the value is defined in the the Base64 alphabet false otherwise. - */ - @Override - protected boolean isInAlphabet(final byte octet) { - return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1; - } - -} \ No newline at end of file diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java b/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java deleted file mode 100644 index d2a2dc33a..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/binary/BaseNCodec.java +++ /dev/null @@ -1,547 +0,0 @@ -package mssql.apache.org.commoncodec.binary; - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.util.Arrays; - -import org.apache.commons.codec.BinaryDecoder; -import org.apache.commons.codec.BinaryEncoder; -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.EncoderException; - -/** - * Abstract superclass for Base-N encoders and decoders. - * - *

- * This class is thread-safe. - *

- * - * @version $Id$ - */ -public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder { - - /** - * Holds thread context so classes can be thread-safe. - * - * This class is not itself thread-safe; each thread must allocate its own copy. - * - * @since 1.7 - */ - static class Context { - - /** - * Place holder for the bytes we're dealing with for our based logic. - * Bitwise operations store and extract the encoding or decoding from this variable. - */ - int ibitWorkArea; - - /** - * Place holder for the bytes we're dealing with for our based logic. - * Bitwise operations store and extract the encoding or decoding from this variable. - */ - long lbitWorkArea; - - /** - * Buffer for streaming. - */ - byte[] buffer; - - /** - * Position where next character should be written in the buffer. - */ - int pos; - - /** - * Position where next character should be read from the buffer. - */ - int readPos; - - /** - * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, - * and must be thrown away. - */ - boolean eof; - - /** - * Variable tracks how many characters have been written to the current line. Only used when encoding. We use - * it to make sure each encoded line never goes beyond lineLength (if lineLength > 0). - */ - int currentLinePos; - - /** - * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This - * variable helps track that. - */ - int modulus; - - Context() { - } - - /** - * Returns a String useful for debugging (especially within a debugger.) - * - * @return a String useful for debugging. - */ - @SuppressWarnings("boxing") // OK to ignore boxing here - @Override - public String toString() { - return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " + - "modulus=%s, pos=%s, readPos=%s]", this.getClass().getSimpleName(), Arrays.toString(buffer), - currentLinePos, eof, ibitWorkArea, lbitWorkArea, modulus, pos, readPos); - } - } - - /** - * EOF - * - * @since 1.7 - */ - static final int EOF = -1; - - /** - * MIME chunk size per RFC 2045 section 6.8. - * - *

- * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any - * equal signs. - *

- * - * @see RFC 2045 section 6.8 - */ - public static final int MIME_CHUNK_SIZE = 76; - - /** - * PEM chunk size per RFC 1421 section 4.3.2.4. - * - *

- * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any - * equal signs. - *

- * - * @see RFC 1421 section 4.3.2.4 - */ - public static final int PEM_CHUNK_SIZE = 64; - - private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; - - /** - * Defines the default buffer size - currently {@value} - * - must be large enough for at least one encoded block+separator - */ - private static final int DEFAULT_BUFFER_SIZE = 8192; - - /** Mask used to extract 8 bits, used in decoding bytes */ - protected static final int MASK_8BITS = 0xff; - - /** - * Byte used to pad output. - */ - protected static final byte PAD_DEFAULT = '='; // Allow static access to default - - /** - * @deprecated Use {@link #pad}. Will be removed in 2.0. - */ - @Deprecated - protected final byte PAD = PAD_DEFAULT; // instance variable just in case it needs to vary later - - protected final byte pad; // instance variable just in case it needs to vary later - - /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ - private final int unencodedBlockSize; - - /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ - private final int encodedBlockSize; - - /** - * Chunksize for encoding. Not used when decoding. - * A value of zero or less implies no chunking of the encoded data. - * Rounded down to nearest multiple of encodedBlockSize. - */ - protected final int lineLength; - - /** - * Size of chunk separator. Not used unless {@link #lineLength} > 0. - */ - private final int chunkSeparatorLength; - - /** - * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} - * If chunkSeparatorLength is zero, then chunking is disabled. - * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) - * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) - * @param lineLength if > 0, use chunking with a length lineLength - * @param chunkSeparatorLength the chunk separator length, if relevant - */ - protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, - final int lineLength, final int chunkSeparatorLength) { - this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT); - } - - /** - * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize} - * If chunkSeparatorLength is zero, then chunking is disabled. - * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) - * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) - * @param lineLength if > 0, use chunking with a length lineLength - * @param chunkSeparatorLength the chunk separator length, if relevant - * @param pad byte used as padding byte. - */ - protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, - final int lineLength, final int chunkSeparatorLength, final byte pad) { - this.unencodedBlockSize = unencodedBlockSize; - this.encodedBlockSize = encodedBlockSize; - final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; - this.lineLength = useChunking ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; - this.chunkSeparatorLength = chunkSeparatorLength; - - this.pad = pad; - } - - /** - * Returns true if this object has buffered data for reading. - * - * @param context the context to be used - * @return true if there is data still available for reading. - */ - boolean hasData(final Context context) { // package protected for access from I/O streams - return context.buffer != null; - } - - /** - * Returns the amount of buffered data available for reading. - * - * @param context the context to be used - * @return The amount of buffered data available for reading. - */ - int available(final Context context) { // package protected for access from I/O streams - return context.buffer != null ? context.pos - context.readPos : 0; - } - - /** - * Get the default buffer size. Can be overridden. - * - * @return {@link #DEFAULT_BUFFER_SIZE} - */ - protected int getDefaultBufferSize() { - return DEFAULT_BUFFER_SIZE; - } - - /** - * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. - * @param context the context to be used - */ - private byte[] resizeBuffer(final Context context) { - if (context.buffer == null) { - context.buffer = new byte[getDefaultBufferSize()]; - context.pos = 0; - context.readPos = 0; - } else { - final byte[] b = new byte[context.buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; - System.arraycopy(context.buffer, 0, b, 0, context.buffer.length); - context.buffer = b; - } - return context.buffer; - } - - /** - * Ensure that the buffer has room for size bytes - * - * @param size minimum spare space required - * @param context the context to be used - * @return the buffer - */ - protected byte[] ensureBufferSize(final int size, final Context context){ - if ((context.buffer == null) || (context.buffer.length < context.pos + size)){ - return resizeBuffer(context); - } - return context.buffer; - } - - /** - * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail - * bytes. Returns how many bytes were actually extracted. - *

- * Package protected for access from I/O streams. - * - * @param b - * byte[] array to extract the buffered data into. - * @param bPos - * position in byte[] array to start extraction at. - * @param bAvail - * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). - * @param context - * the context to be used - * @return The number of bytes successfully extracted into the provided byte[] array. - */ - int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) { - if (context.buffer != null) { - final int len = Math.min(available(context), bAvail); - System.arraycopy(context.buffer, context.readPos, b, bPos, len); - context.readPos += len; - if (context.readPos >= context.pos) { - context.buffer = null; // so hasData() will return false, and this method can return -1 - } - return len; - } - return context.eof ? EOF : 0; - } - - /** - * Checks if a byte value is whitespace or not. - * Whitespace is taken to mean: space, tab, CR, LF - * @param byteToCheck - * the byte to check - * @return true if byte is whitespace, false otherwise - */ - protected static boolean isWhiteSpace(final byte byteToCheck) { - switch (byteToCheck) { - case ' ' : - case '\n' : - case '\r' : - case '\t' : - return true; - default : - return false; - } - } - - /** - * Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of - * the Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[]. - * - * @param obj - * Object to encode - * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied. - * @throws EncoderException - * if the parameter supplied is not of type byte[] - */ - @Override - public Object encode(final Object obj) throws EncoderException { - if (!(obj instanceof byte[])) { - throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]"); - } - return encode((byte[]) obj); - } - - /** - * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet. - * Uses UTF8 encoding. - * - * @param pArray - * a byte array containing binary data - * @return A String containing only Base-N character data - */ - public String encodeToString(final byte[] pArray) { - return StringUtils.newStringUtf8(encode(pArray)); - } - - /** - * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. - * Uses UTF8 encoding. - * - * @param pArray a byte array containing binary data - * @return String containing only character data in the appropriate alphabet. - * @since 1.5 - * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring. - */ - public String encodeAsString(final byte[] pArray){ - return StringUtils.newStringUtf8(encode(pArray)); - } - - /** - * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of - * the Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String. - * - * @param obj - * Object to decode - * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String - * supplied. - * @throws DecoderException - * if the parameter supplied is not of type byte[] - */ - @Override - public Object decode(final Object obj) throws DecoderException { - if (obj instanceof byte[]) { - return decode((byte[]) obj); - } else if (obj instanceof String) { - return decode((String) obj); - } else { - throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String"); - } - } - - /** - * Decodes a String containing characters in the Base-N alphabet. - * - * @param pArray - * A String containing Base-N character data - * @return a byte array containing binary data - */ - public byte[] decode(final String pArray) { - return decode(StringUtils.getBytesUtf8(pArray)); - } - - /** - * Decodes a byte[] containing characters in the Base-N alphabet. - * - * @param pArray - * A byte array containing Base-N character data - * @return a byte array containing binary data - */ - @Override - public byte[] decode(final byte[] pArray) { - if (pArray == null || pArray.length == 0) { - return pArray; - } - final Context context = new Context(); - decode(pArray, 0, pArray.length, context); - decode(pArray, 0, EOF, context); // Notify decoder of EOF. - final byte[] result = new byte[context.pos]; - readResults(result, 0, result.length, context); - return result; - } - - /** - * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. - * - * @param pArray - * a byte array containing binary data - * @return A byte array containing only the base N alphabetic character data - */ - @Override - public byte[] encode(final byte[] pArray) { - if (pArray == null || pArray.length == 0) { - return pArray; - } - return encode(pArray, 0, pArray.length); - } - - /** - * Encodes a byte[] containing binary data, into a byte[] containing - * characters in the alphabet. - * - * @param pArray - * a byte array containing binary data - * @param offset - * initial offset of the subarray. - * @param length - * length of the subarray. - * @return A byte array containing only the base N alphabetic character data - * @since 1.11 - */ - public byte[] encode(final byte[] pArray, int offset, int length) { - if (pArray == null || pArray.length == 0) { - return pArray; - } - final Context context = new Context(); - encode(pArray, offset, length, context); - encode(pArray, offset, EOF, context); // Notify encoder of EOF. - final byte[] buf = new byte[context.pos - context.readPos]; - readResults(buf, 0, buf.length, context); - return buf; - } - - // package protected for access from I/O streams - abstract void encode(byte[] pArray, int i, int length, Context context); - - // package protected for access from I/O streams - abstract void decode(byte[] pArray, int i, int length, Context context); - - /** - * Returns whether or not the octet is in the current alphabet. - * Does not allow whitespace or pad. - * - * @param value The value to test - * - * @return true if the value is defined in the current alphabet, false otherwise. - */ - protected abstract boolean isInAlphabet(byte value); - - /** - * Tests a given byte array to see if it contains only valid characters within the alphabet. - * The method optionally treats whitespace and pad as valid. - * - * @param arrayOctet byte array to test - * @param allowWSPad if true, then whitespace and PAD are also allowed - * - * @return true if all bytes are valid characters in the alphabet or if the byte array is empty; - * false, otherwise - */ - public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) { - for (byte octet : arrayOctet) { - if (!isInAlphabet(octet) && - (!allowWSPad || (octet != pad) && !isWhiteSpace(octet))) { - return false; - } - } - return true; - } - - /** - * Tests a given String to see if it contains only valid characters within the alphabet. - * The method treats whitespace and PAD as valid. - * - * @param basen String to test - * @return true if all characters in the String are valid characters in the alphabet or if - * the String is empty; false, otherwise - * @see #isInAlphabet(byte[], boolean) - */ - public boolean isInAlphabet(final String basen) { - return isInAlphabet(StringUtils.getBytesUtf8(basen), true); - } - - /** - * Tests a given byte array to see if it contains any characters within the alphabet or PAD. - * - * Intended for use in checking line-ending arrays - * - * @param arrayOctet - * byte array to test - * @return true if any byte is a valid character in the alphabet or PAD; false otherwise - */ - protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { - if (arrayOctet == null) { - return false; - } - for (final byte element : arrayOctet) { - if (pad == element || isInAlphabet(element)) { - return true; - } - } - return false; - } - - /** - * Calculates the amount of space needed to encode the supplied array. - * - * @param pArray byte[] array which will later be encoded - * - * @return amount of space needed to encoded the supplied array. - * Returns a long since a max-len array will require > Integer.MAX_VALUE - */ - public long getEncodedLength(final byte[] pArray) { - // Calculate non-chunked size - rounded up to allow for padding - // cast to long is needed to avoid possibility of overflow - long len = ((pArray.length + unencodedBlockSize-1) / unencodedBlockSize) * (long) encodedBlockSize; - if (lineLength > 0) { // We're using chunking - // Round up to nearest multiple - len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength; - } - return len; - } -} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java b/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java deleted file mode 100644 index 3580b6166..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/binary/CharSequenceUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package mssql.apache.org.commoncodec.binary; - -/** - *

- * Operations on {@link CharSequence} that are null safe. - *

- *

- * Copied from Apache Commons Lang r1586295 on April 10, 2014 (day of 3.3.2 release). - *

- * - * @see CharSequence - * @since 1.10 - */ -public class CharSequenceUtils { - - /** - * Green implementation of regionMatches. - * - * @param cs - * the CharSequence to be processed - * @param ignoreCase - * whether or not to be case insensitive - * @param thisStart - * the index to start on the cs CharSequence - * @param substring - * the CharSequence to be looked for - * @param start - * the index to start on the substring CharSequence - * @param length - * character length of the region - * @return whether the region matched - */ - static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, - final CharSequence substring, final int start, final int length) { - if (cs instanceof String && substring instanceof String) { - return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); - } - int index1 = thisStart; - int index2 = start; - int tmpLen = length; - - while (tmpLen-- > 0) { - final char c1 = cs.charAt(index1++); - final char c2 = substring.charAt(index2++); - - if (c1 == c2) { - continue; - } - - if (!ignoreCase) { - return false; - } - - // The same check as in String.regionMatches(): - if (Character.toUpperCase(c1) != Character.toUpperCase(c2) && - Character.toLowerCase(c1) != Character.toLowerCase(c2)) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java b/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java deleted file mode 100644 index 3e2cd44fc..000000000 --- a/src/main/java/mssql/apache/org/commoncodec/binary/StringUtils.java +++ /dev/null @@ -1,420 +0,0 @@ -package mssql.apache.org.commoncodec.binary; - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -import org.apache.commons.codec.CharEncoding; -import org.apache.commons.codec.Charsets; - -/** - * Converts String to and from bytes using the encodings required by the Java specification. These encodings are - * specified in - * Standard charsets. - * - *

This class is immutable and thread-safe.

- * - * @see CharEncoding - * @see Standard charsets - * @version $Id$ - * @since 1.4 - */ -public class StringUtils { - - /** - *

- * Compares two CharSequences, returning true if they represent equal sequences of characters. - *

- * - *

- * nulls are handled without exceptions. Two null references are considered to be equal. - * The comparison is case sensitive. - *

- * - *
-     * StringUtils.equals(null, null)   = true
-     * StringUtils.equals(null, "abc")  = false
-     * StringUtils.equals("abc", null)  = false
-     * StringUtils.equals("abc", "abc") = true
-     * StringUtils.equals("abc", "ABC") = false
-     * 
- * - *

- * Copied from Apache Commons Lang r1583482 on April 10, 2014 (day of 3.3.2 release). - *

- * - * @see Object#equals(Object) - * @param cs1 - * the first CharSequence, may be null - * @param cs2 - * the second CharSequence, may be null - * @return true if the CharSequences are equal (case-sensitive), or both null - * @since 1.10 - */ - public static boolean equals(final CharSequence cs1, final CharSequence cs2) { - if (cs1 == cs2) { - return true; - } - if (cs1 == null || cs2 == null) { - return false; - } - if (cs1 instanceof String && cs2 instanceof String) { - return cs1.equals(cs2); - } - return cs1.length() == cs2.length() && CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, cs1.length()); - } - - /** - * Calls {@link String#getBytes(Charset)} - * - * @param string - * The string to encode (if null, return null). - * @param charset - * The {@link Charset} to encode the String - * @return the encoded bytes - */ - private static byte[] getBytes(final String string, final Charset charset) { - if (string == null) { - return null; - } - return string.getBytes(charset); - } - - /** - * Calls {@link String#getBytes(Charset)} - * - * @param string - * The string to encode (if null, return null). - * @param charset - * The {@link Charset} to encode the String - * @return the encoded bytes - */ - private static ByteBuffer getByteBuffer(final String string, final Charset charset) { - if (string == null) { - return null; - } - return ByteBuffer.wrap(string.getBytes(charset)); - } - - /** - * Encodes the given string into a byte buffer using the UTF-8 charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - * @since 1.11 - */ - public static ByteBuffer getByteBufferUtf8(final String string) { - return getByteBuffer(string, Charsets.UTF_8); - } - - /** - * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new - * byte array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesIso8859_1(final String string) { - return getBytes(string, Charsets.ISO_8859_1); - } - - - /** - * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte - * array. - *

- * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which - * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. - *

- * - * @param string - * the String to encode, may be null - * @param charsetName - * The name of a required {@link java.nio.charset.Charset} - * @return encoded bytes, or null if the input string was null - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a - * required charset name. - * @see CharEncoding - * @see String#getBytes(String) - */ - public static byte[] getBytesUnchecked(final String string, final String charsetName) { - if (string == null) { - return null; - } - try { - return string.getBytes(charsetName); - } catch (final UnsupportedEncodingException e) { - throw StringUtils.newIllegalStateException(charsetName, e); - } - } - - /** - * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUsAscii(final String string) { - return getBytes(string, Charsets.US_ASCII); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16(final String string) { - return getBytes(string, Charsets.UTF_16); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16Be(final String string) { - return getBytes(string, Charsets.UTF_16BE); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf16Le(final String string) { - return getBytes(string, Charsets.UTF_16LE); - } - - /** - * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte - * array. - * - * @param string - * the String to encode, may be null - * @return encoded bytes, or null if the input string was null - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - * @see Standard charsets - * @see #getBytesUnchecked(String, String) - */ - public static byte[] getBytesUtf8(final String string) { - return getBytes(string, Charsets.UTF_8); - } - - private static IllegalStateException newIllegalStateException(final String charsetName, - final UnsupportedEncodingException e) { - return new IllegalStateException(charsetName + ": " + e); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the given charset. - * - * @param bytes - * The bytes to be decoded into characters - * @param charset - * The {@link Charset} to encode the String; not {@code null} - * @return A new String decoded from the specified array of bytes using the given charset, - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if charset is {@code null} - */ - private static String newString(final byte[] bytes, final Charset charset) { - return bytes == null ? null : new String(bytes, charset); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the given charset. - *

- * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which - * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. - *

- * - * @param bytes - * The bytes to be decoded into characters, may be null - * @param charsetName - * The name of a required {@link java.nio.charset.Charset} - * @return A new String decoded from the specified array of bytes using the given charset, - * or null if the input byte array was null. - * @throws IllegalStateException - * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a - * required charset name. - * @see CharEncoding - * @see String#String(byte[], String) - */ - public static String newString(final byte[] bytes, final String charsetName) { - if (bytes == null) { - return null; - } - try { - return new String(bytes, charsetName); - } catch (final UnsupportedEncodingException e) { - throw StringUtils.newIllegalStateException(charsetName, e); - } - } - - /** - * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. - * - * @param bytes - * The bytes to be decoded into characters, may be null - * @return A new String decoded from the specified array of bytes using the ISO-8859-1 charset, or - * null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringIso8859_1(final byte[] bytes) { - return newString(bytes, Charsets.ISO_8859_1); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the US-ASCII charset, - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringUsAscii(final byte[] bytes) { - return newString(bytes, Charsets.US_ASCII); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the UTF-16 charset - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringUtf16(final byte[] bytes) { - return newString(bytes, Charsets.UTF_16); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the UTF-16BE charset, - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringUtf16Be(final byte[] bytes) { - return newString(bytes, Charsets.UTF_16BE); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the UTF-16LE charset, - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringUtf16Le(final byte[] bytes) { - return newString(bytes, Charsets.UTF_16LE); - } - - /** - * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. - * - * @param bytes - * The bytes to be decoded into characters - * @return A new String decoded from the specified array of bytes using the UTF-8 charset, - * or null if the input byte array was null. - * @throws NullPointerException - * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is - * required by the Java platform specification. - * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException - */ - public static String newStringUtf8(final byte[] bytes) { - return newString(bytes, Charsets.UTF_8); - } - -} \ No newline at end of file From 778d3ee1c16d04fff326edb3ed92f7d3d4903175 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Tue, 28 Nov 2017 10:03:07 -0800 Subject: [PATCH 18/39] updated dependency versions in pom file for jdbc 4.3 branch --- pom.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e1203c534..3973b6dc2 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ UTF-8 - 1.0.0-M3 + 1.1.0-M1 5.0.0-M3 @@ -54,7 +54,7 @@ com.microsoft.azure adal4j - 1.2.0 + 1.3.0 true @@ -117,7 +117,7 @@ com.zaxxer HikariCP - 2.6.1 + 2.7.4 test @@ -190,8 +190,8 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - 1.9 - 1.9 + 9 + 9
@@ -276,7 +276,7 @@ org.apache.felix maven-bundle-plugin - 3.2.0 + 3.3.0 true From 27b94141e966364400f5c8ee13058198987bbf60 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Thu, 30 Nov 2017 10:39:56 -0800 Subject: [PATCH 19/39] update samples in JDBC 4.3 branch + change the the junit.platform.version --- pom.xml | 2 +- .../src/main/java/AlwaysEncrypted.java | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 3973b6dc2..e4b90953b 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ UTF-8 - 1.1.0-M1 + 1.0.0-M3 5.0.0-M3 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. From 7ec7356fbad35536b119b91cbc39d2df887c4f87 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Tue, 5 Dec 2017 17:58:53 -0800 Subject: [PATCH 20/39] remove deprecated APIs --- .../java/com/microsoft/sqlserver/jdbc/DDC.java | 4 ++-- .../com/microsoft/sqlserver/jdbc/FailOverInfo.java | 2 +- .../sqlserver/jdbc/SQLServerBulkCSVFileRecord.java | 2 +- .../sqlserver/jdbc/SQLServerConnection.java | 14 +++++++------- .../sqlserver/jdbc/SQLServerDataSource.java | 2 +- .../jdbc/SQLServerDataSourceObjectFactory.java | 5 +++-- .../microsoft/sqlserver/jdbc/SQLServerDriver.java | 2 +- .../microsoft/sqlserver/jdbc/SQLServerSQLXML.java | 10 ++++++---- .../com/microsoft/sqlserver/jdbc/SqlVariant.java | 2 +- .../java/com/microsoft/sqlserver/jdbc/dtv.java | 2 +- 10 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java b/src/main/java/com/microsoft/sqlserver/jdbc/DDC.java index 77891c37b..07967b634 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 (new 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 (new 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/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 4ff063dab..e429dab44 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -575,7 +575,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/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 3bd137fc0..92c956393 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -1422,7 +1422,7 @@ Connection connectInternal(Properties propsIn, sPropKey = SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString(); if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); this.setStatementPoolingCacheSize(n); } catch (NumberFormatException e) { @@ -1559,7 +1559,7 @@ Connection connectInternal(Properties propsIn, try { String strPort = activeConnectionProperties.getProperty(sPropKey); if (null != strPort) { - nPort = new Integer(strPort); + nPort = Integer.parseInt(strPort); if ((nPort < 0) || (nPort > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); @@ -1639,7 +1639,7 @@ else if (0 == requestedPacketSize) nLockTimeout = defaultLockTimeOut; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultLockTimeOut) nLockTimeout = n; else { @@ -1660,7 +1660,7 @@ else if (0 == requestedPacketSize) queryTimeoutSeconds = defaultQueryTimeout; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultQueryTimeout) { queryTimeoutSeconds = n; } @@ -1682,7 +1682,7 @@ else if (0 == requestedPacketSize) socketTimeoutMilliseconds = defaultSocketTimeout; // Wait forever if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); if (n >= defaultSocketTimeout) { socketTimeoutMilliseconds = n; } @@ -1702,7 +1702,7 @@ else if (0 == requestedPacketSize) sPropKey = SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString(); if (activeConnectionProperties.getProperty(sPropKey) != null && activeConnectionProperties.getProperty(sPropKey).length() > 0) { try { - int n = new Integer(activeConnectionProperties.getProperty(sPropKey)); + int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey)); setServerPreparedStatementDiscardThreshold(n); } catch (NumberFormatException e) { @@ -2163,7 +2163,7 @@ ServerPortPlaceHolder primaryPermissionCheck(String primary, connectionlogger.fine(toString() + " SQL Server port returned by SQL Browser: " + instancePort); try { if (null != instancePort) { - primaryPortNumber = new Integer(instancePort); + primaryPortNumber = Integer.parseInt(instancePort); if ((primaryPortNumber < 0) || (primaryPortNumber > 65535)) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 9c4ebb2bb..cfb1081cb 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -866,7 +866,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, new Integer(propValue).toString()); + props.setProperty(propKey, Integer.valueOf(propValue).toString()); loggerExternal.exiting(getClassNameLogging(), "set" + propKey); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java index 761eb26ae..f52c46b33 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; @@ -35,7 +36,7 @@ public SQLServerDataSourceObjectFactory() { public Object getObjectInstance(Object ref, Name name, Context c, - Hashtable h) throws SQLServerException { + Hashtable h) throws SQLServerException, InvocationTargetException, NoSuchMethodException, SecurityException { // Create a new instance of a DataSource class from the given reference. try { javax.naming.Reference r = (javax.naming.Reference) ref; @@ -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. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 1dfeae276..1063f903e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -632,7 +632,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(), new 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/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/SqlVariant.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlVariant.java index 2d4c13436..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 = {new Integer(intValue)}; + Object[] msgArgs = {Integer.valueOf(intValue)}; throw new IllegalArgumentException(form.format(msgArgs)); } 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); From 3a213461005cee0e9d49d8632b21113832f1b2a9 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Thu, 7 Dec 2017 11:07:07 -0800 Subject: [PATCH 21/39] add more catch blocks for new getDeclaredConstructor method. --- .../jdbc/SQLServerDataSourceObjectFactory.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java index f52c46b33..6518d7057 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSourceObjectFactory.java @@ -36,7 +36,7 @@ public SQLServerDataSourceObjectFactory() { public Object getObjectInstance(Object ref, Name name, Context c, - Hashtable h) throws SQLServerException, InvocationTargetException, NoSuchMethodException, SecurityException { + Hashtable h) throws SQLServerException { // Create a new instance of a DataSource class from the given reference. try { javax.naming.Reference r = (javax.naming.Reference) ref; @@ -80,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; From 9540cce8e9940819ccbf17971b337fb87b1ce137 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Fri, 8 Dec 2017 09:59:58 -0800 Subject: [PATCH 22/39] update Travis script with official support of jdk 9 --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6df16a136..adbbcecd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ sudo: required language: java -sudo: false -dist: trusty -jdk: oraclejdk9 +jdk: + - oraclejdk9 addons: apt: @@ -35,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: @@ -46,5 +45,5 @@ 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 + - 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 From 24486c9b3d4d4ac6483bcb1629c39a338d362dc6 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Fri, 8 Dec 2017 10:01:40 -0800 Subject: [PATCH 23/39] fix comment --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index adbbcecd5..a9a2d8f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,6 +44,6 @@ before_script: script: - docker ps -a -##Test for JDBC Specification 41 & 42 and submit coverage report. +##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 From 47ddc431d720b6c17794b63b0e4d195aefed48c6 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Fri, 8 Dec 2017 10:38:42 -0800 Subject: [PATCH 24/39] update readme file --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95907aa9d..0178b4499 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. From e601b81dc84a7b17957d16510c3babaebe5d64b9 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Dec 2017 15:32:40 -0800 Subject: [PATCH 25/39] use javax.security.auth.kerberos instead of sun.security.krb5.Credentials --- .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 31 ++++++------------- .../sqlserver/jdbc/SQLServerResource.java | 1 - 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index a2cf711a0..9fd0e1abf 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -7,7 +7,8 @@ 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,9 +16,6 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection.ActiveDirectoryAuthentication; import com.microsoft.sqlserver.jdbc.SQLServerConnection.SqlFedAuthInfo; -import sun.security.krb5.Credentials; -import sun.security.krb5.KrbException; - class SQLServerADAL4JUtils { static final private java.util.logging.Logger adal4jLogger = java.util.logging.Logger @@ -66,38 +64,27 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, String authenticationString) throws SQLServerException { ExecutorService executorService = Executors.newFixedThreadPool(1); - String tgtClientName = null; - try { - // Get Kerberos TGT ticket and retrieve client name. - // If KRB5CCNAME environment variable is not set, the method searches for default location - Credentials cred = Credentials.acquireTGTFromCache(null, System.getenv("KRB5CCNAME")); - - if (null == cred) { - throw new SQLServerException(SQLServerException.getErrString("R_AADIntegratedTGTNotFound"), null); - } - - tgtClientName = cred.getClient().toString(); - - if (adal4jLogger.isLoggable(Level.FINE)) { - adal4jLogger.fine(adal4jLogger.toString() + " client name of Kerberos TGT is:" + tgtClientName); - } + // principal name does not matter, what matters is the realm name + // it gets the username in principal_name@realm_name format + KerberosPrincipal kp = new KerberosPrincipal("username"); + String username = kp.getName(); AuthenticationContext context = new AuthenticationContext(fedAuthInfo.stsurl, false, executorService); Future future = context.acquireToken(fedAuthInfo.spn, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, - tgtClientName, null, null); + username, null, null); AuthenticationResult authenticationResult = future.get(); SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); return fedAuthToken; } - catch (InterruptedException | IOException | KrbException e) { + catch (InterruptedException | IOException e) { throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); - Object[] msgArgs = {tgtClientName, authenticationString}; + Object[] msgArgs = {"", authenticationString}; if (null == e.getCause() || null == e.getCause().getMessage()) { // the case when Future's outcome has no AuthenticationResult but exception diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index f4a0896d3..995e8b79f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -391,6 +391,5 @@ protected Object[][] getContents() { {"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_AADIntegratedTGTNotFound", "Kerberos ticket-granting ticket (TGT) cannot be found."}, }; } \ No newline at end of file From 8070803fced298640087a8da4c467a8842bba285 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Dec 2017 16:37:31 -0800 Subject: [PATCH 26/39] add log --- .../microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index 9fd0e1abf..096543ffd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -7,6 +7,7 @@ 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; @@ -67,8 +68,12 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, try { // principal name does not matter, what matters is the realm name // it gets the username in principal_name@realm_name format - KerberosPrincipal kp = new KerberosPrincipal("username"); - String username = kp.getName(); + 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, From 83ec122dee63b190ef09acc7168a0b67316d3410 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Wed, 13 Dec 2017 10:40:13 -0800 Subject: [PATCH 27/39] remove JNI functions for AAD integrated auth --- .../sqlserver/jdbc/AuthenticationJNI.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java index d86d481f4..9b87d6062 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java @@ -79,16 +79,6 @@ static int GetMaxSSPIBlobSize() { port = serverport; } - static FedAuthDllInfo getAccessTokenForWindowsIntegrated(String stsURL, - String servicePrincipalName, - String clientConnectionId, - String clientId, - long expirationFileTime) throws DLLException { - FedAuthDllInfo dllInfo = ADALGetAccessTokenForWindowsIntegrated(stsURL, servicePrincipalName, clientConnectionId, clientId, - expirationFileTime, authLogger); - return dllInfo; - } - // InitDNSName should be called to initialize the DNSName before calling this function byte[] GenerateClientContext(byte[] pin, boolean[] done) throws SQLServerException { @@ -169,13 +159,6 @@ private native static int GetDNSName(String address, String[] DNSName, java.util.logging.Logger log); - private native static FedAuthDllInfo ADALGetAccessTokenForWindowsIntegrated(String stsURL, - String servicePrincipalName, - String clientConnectionId, - String clientId, - long expirationFileTime, - java.util.logging.Logger log); - native static byte[] DecryptColumnEncryptionKey(String masterKeyPath, String encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) throws DLLException; From 6ccf2014192c644e55f0ae96ad064e2425a7a293 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Wed, 13 Dec 2017 12:50:43 -0800 Subject: [PATCH 28/39] update the ADAL4J version to 1.4.0 in pom, this will break the compilation for now. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 58aa5d4ab..6a6cff00b 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ com.microsoft.azure adal4j - 1.3.0 + 1.4.0 true From 257d6e1ba389cbb1c792fdadc572d6247ca95c82 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 18 Dec 2017 13:43:36 -0800 Subject: [PATCH 29/39] use wrapper for sharding APIs --- .../jdbc/ISQLServerConnection43.java | 20 ++++++++++ .../jdbc/ISQLServerDataSource43.java | 12 ++++++ .../sqlserver/jdbc/SQLServerConnection.java | 25 ------------- .../sqlserver/jdbc/SQLServerConnection43.java | 37 +++++++++++++++++++ .../sqlserver/jdbc/SQLServerDataSource.java | 19 ++++------ .../sqlserver/jdbc/SQLServerDataSource43.java | 24 ++++++++++++ .../sqlserver/jdbc/SQLServerDriver.java | 7 +++- .../sqlserver/jdbc/SQLServerXAConnection.java | 8 +++- .../com/microsoft/sqlserver/jdbc/Util.java | 22 +++++++++++ 9 files changed, 135 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java 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..13695e703 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -0,0 +1,20 @@ +package com.microsoft.sqlserver.jdbc; + +import java.sql.SQLFeatureNotSupportedException; +import java.sql.ShardingKey; + +public interface ISQLServerConnection43 extends ISQLServerConnection { + + public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException; + + public void setShardingKey(ShardingKey shardingKey, + ShardingKey superShardingKey) throws SQLFeatureNotSupportedException; + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + int timeout) throws SQLFeatureNotSupportedException; + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + ShardingKey superShardingKey, + int timeout) throws SQLFeatureNotSupportedException; + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java new file mode 100644 index 000000000..47cbe003c --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java @@ -0,0 +1,12 @@ +package com.microsoft.sqlserver.jdbc; + +import java.sql.ConnectionBuilder; +import java.sql.SQLException; +import java.sql.ShardingKeyBuilder; + +public interface ISQLServerDataSource43 extends ISQLServerDataSource { + + public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException; + + public ConnectionBuilder createConnectionBuilder() throws SQLException; +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 34e72b207..3bc6e8c66 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -33,7 +33,6 @@ import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; -import java.sql.ShardingKey; import java.sql.Statement; import java.sql.Struct; import java.text.MessageFormat; @@ -5259,30 +5258,6 @@ public void endRequest() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException("endRequest not implemented"); } - public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); - } - - public void setShardingKey(ShardingKey shardingKey, - ShardingKey superShardingKey) throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); - } - - public boolean setShardingKeyIfValid(ShardingKey shardingKey, - int timeout) throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); - } - - public boolean setShardingKeyIfValid(ShardingKey shardingKey, - ShardingKey superShardingKey, - int timeout) throws SQLFeatureNotSupportedException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder 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..44e348d72 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -0,0 +1,37 @@ +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); + // TODO Auto-generated constructor stub + } + + public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public void setShardingKey(ShardingKey shardingKey, + ShardingKey superShardingKey) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + int timeout) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public boolean setShardingKeyIfValid(ShardingKey shardingKey, + ShardingKey superShardingKey, + int timeout) throws SQLFeatureNotSupportedException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder 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 cfb1081cb..ebe4fee35 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -10,10 +10,8 @@ import java.io.PrintWriter; import java.sql.Connection; -import java.sql.ConnectionBuilder; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.ShardingKeyBuilder; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; @@ -1005,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()); @@ -1107,15 +1111,6 @@ public T unwrap(Class iface) throws SQLException { return t; } - public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); - } - - public ConnectionBuilder createConnectionBuilder() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented"); - } // Returns unique id for each DataSource instance. private static int nextDataSourceID() { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java new file mode 100644 index 000000000..7d4621e87 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java @@ -0,0 +1,24 @@ +package com.microsoft.sqlserver.jdbc; + +import java.sql.ConnectionBuilder; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.ShardingKeyBuilder; + +public class SQLServerDataSource43 extends SQLServerDataSource implements ISQLServerDataSource43{ + + public SQLServerDataSource43 () throws SQLServerException { + super(); + } + + public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + } + + public ConnectionBuilder createConnectionBuilder() throws SQLException { + DriverJDBCVersion.checkSupportsJDBC43(); + throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented"); + } + +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 1063f903e..d7480d5e1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -612,7 +612,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); 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/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index c1d6d81dc..15d5b81d9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -1008,6 +1008,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 { From 6a833eb99d744343c59bfe70912c614ef8ca678a Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 18 Dec 2017 14:40:57 -0800 Subject: [PATCH 30/39] fix another issue with Java 9 regarding bulkcopy connection check --- .../java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java | 3 ++- .../java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java index 2667d5af0..f15c2846e 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; @@ -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/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); } From 4b28d7084a8e405efa0a2fb81d0c2f294222ea84 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 18 Dec 2017 18:20:01 -0800 Subject: [PATCH 31/39] added another fix --- .../java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java index f15c2846e..e30dd640c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java @@ -183,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 From 19d8a5157e31c0eeeabd6fa9e2afccb87717ff91 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Mon, 18 Dec 2017 18:31:40 -0800 Subject: [PATCH 32/39] added cast to Buffer --- .../com/microsoft/sqlserver/jdbc/ReaderInputStream.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java index e30dd640c..1a2513816 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ReaderInputStream.java @@ -195,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 @@ -219,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 @@ -229,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; @@ -242,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 From 99868e6d31e7b78232e18e50a23c12d9fb3177fe Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Tue, 19 Dec 2017 16:12:47 -0800 Subject: [PATCH 33/39] change Base64.getEncoder().encodeToString() --- .../jdbc/ISQLServerDataSource43.java | 12 ---------- .../SQLServerAeadAes256CbcHmac256Factory.java | 2 +- ...umnEncryptionCertificateStoreProvider.java | 3 ++- .../sqlserver/jdbc/SQLServerDataSource43.java | 24 ------------------- .../jdbc/SQLServerSymmetricKeyCache.java | 2 +- 5 files changed, 4 insertions(+), 39 deletions(-) delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java deleted file mode 100644 index 47cbe003c..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource43.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.microsoft.sqlserver.jdbc; - -import java.sql.ConnectionBuilder; -import java.sql.SQLException; -import java.sql.ShardingKeyBuilder; - -public interface ISQLServerDataSource43 extends ISQLServerDataSource { - - public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException; - - public ConnectionBuilder createConnectionBuilder() throws SQLException; -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java index 1c3e1cd7e..3a170da2d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerAeadAes256CbcHmac256Factory.java @@ -37,7 +37,7 @@ SQLServerEncryptionAlgorithm create(SQLServerSymmetricKey columnEncryptionKey, } StringBuilder factoryKeyBuilder = new StringBuilder(); - factoryKeyBuilder.append(Base64.getEncoder().encode(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/SQLServerColumnEncryptionCertificateStoreProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java index d4cddff78..5f8bc8001 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionCertificateStoreProvider.java @@ -22,6 +22,7 @@ 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; @@ -139,7 +140,7 @@ private String getThumbPrint(X509Certificate cert) throws NoSuchAlgorithmExcepti byte[] der = cert.getEncoded(); md.update(der); byte[] digest = md.digest(); - return Util.bytesToHexString(digest, digest.length); + return Base64.getEncoder().encodeToString(digest); } private CertificateDetails getCertificateByThumbprint(String storeLocation, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java deleted file mode 100644 index 7d4621e87..000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource43.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.microsoft.sqlserver.jdbc; - -import java.sql.ConnectionBuilder; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.ShardingKeyBuilder; - -public class SQLServerDataSource43 extends SQLServerDataSource implements ISQLServerDataSource43{ - - public SQLServerDataSource43 () throws SQLServerException { - super(); - } - - public ShardingKeyBuilder createShardingKeyBuilder() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); - } - - public ConnectionBuilder createConnectionBuilder() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createConnectionBuilder not implemented"); - } - -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java index 819293f7b..04f9335ff 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java @@ -98,7 +98,7 @@ SQLServerSymmetricKey getKey(EncryptionKeyInfo keyInfo, String keyLookupValue; keyLookupValuebuffer.append(":"); - keyLookupValuebuffer.append(Base64.getEncoder().encode((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); From b9fc8015a6344cf71de756cfceb3a5d4c402acbc Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Wed, 20 Dec 2017 14:14:50 -0800 Subject: [PATCH 34/39] Make sure the right wrapper is used for 4.2 and above. --- .../sqlserver/jdbc/SQLServerConnection.java | 17 +++++++++++------ .../sqlserver/jdbc/SQLServerStatement.java | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 3bc6e8c66..af9d3c329 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3186,8 +3186,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 +3212,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 +3234,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); } @@ -4655,7 +4658,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 +4694,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 { 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 { From 8faa9123f22ce92c5255e5e69ab6820ff48d6846 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Wed, 20 Dec 2017 14:59:20 -0800 Subject: [PATCH 35/39] make SQLServerConnection43 throw SQLServerException for new Unsupported APIs --- .../jdbc/ISQLServerConnection43.java | 9 ++-- .../sqlserver/jdbc/SQLServerConnection43.java | 17 +++---- .../microsoft/sqlserver/jdbc/JDBC43Test.java | 51 +++++++++++++++++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java index 13695e703..1689cde87 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -1,20 +1,19 @@ package com.microsoft.sqlserver.jdbc; -import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKey; public interface ISQLServerConnection43 extends ISQLServerConnection { - public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException; + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException; public void setShardingKey(ShardingKey shardingKey, - ShardingKey superShardingKey) throws SQLFeatureNotSupportedException; + ShardingKey superShardingKey) throws SQLServerException; public boolean setShardingKeyIfValid(ShardingKey shardingKey, - int timeout) throws SQLFeatureNotSupportedException; + int timeout) throws SQLServerException; public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, - int timeout) throws SQLFeatureNotSupportedException; + int timeout) throws SQLServerException; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java index 44e348d72..217bcfc72 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -7,31 +7,30 @@ public class SQLServerConnection43 extends SQLServerConnection implements ISQLSe SQLServerConnection43(String parentInfo) throws SQLServerException { super(parentInfo); - // TODO Auto-generated constructor stub } - public void setShardingKey(ShardingKey shardingKey) throws SQLFeatureNotSupportedException { + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); } public void setShardingKey(ShardingKey shardingKey, - ShardingKey superShardingKey) throws SQLFeatureNotSupportedException { + ShardingKey superShardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")) ; } public boolean setShardingKeyIfValid(ShardingKey shardingKey, - int timeout) throws SQLFeatureNotSupportedException { + int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, - int timeout) throws SQLFeatureNotSupportedException { + int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("createShardingKeyBuilder not implemented"); + throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java index b4ac96682..6873bbffe 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -29,6 +29,7 @@ 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; /** @@ -139,6 +140,56 @@ public void connectionPoolDataSourceTest() throws TestAbortedException, SQLExcep 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 From daa647c62cff1576bb1fe9df7827d1d0fca8cd70 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 2 Jan 2018 15:20:05 -0800 Subject: [PATCH 36/39] & is not allowed in error strings or parsing fails. --- .../java/com/microsoft/sqlserver/jdbc/SQLServerResource.java | 4 ++-- .../microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 63e701505..21434ba6a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -390,7 +390,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/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()); } } From b567e72f71c8576d19b26b0dcebd290410b01cb1 Mon Sep 17 00:00:00 2001 From: Afsaneh Rafighi Date: Fri, 12 Jan 2018 17:07:50 -0800 Subject: [PATCH 37/39] use dll authentication when DLL is loaded and windows is the operating system --- .../sqlserver/jdbc/AuthenticationJNI.java | 21 ++++++ .../sqlserver/jdbc/SQLServerConnection.java | 75 ++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java index 9b87d6062..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; @@ -79,6 +83,16 @@ static int GetMaxSSPIBlobSize() { port = serverport; } + static FedAuthDllInfo getAccessTokenForWindowsIntegrated(String stsURL, + String servicePrincipalName, + String clientConnectionId, + String clientId, + long expirationFileTime) throws DLLException { + FedAuthDllInfo dllInfo = ADALGetAccessTokenForWindowsIntegrated(stsURL, servicePrincipalName, clientConnectionId, clientId, + expirationFileTime, authLogger); + return dllInfo; + } + // InitDNSName should be called to initialize the DNSName before calling this function byte[] GenerateClientContext(byte[] pin, boolean[] done) throws SQLServerException { @@ -159,6 +173,13 @@ private native static int GetDNSName(String address, String[] DNSName, java.util.logging.Logger log); + private native static FedAuthDllInfo ADALGetAccessTokenForWindowsIntegrated(String stsURL, + String servicePrincipalName, + String clientConnectionId, + String clientId, + long expirationFileTime, + java.util.logging.Logger log); + native static byte[] DecryptColumnEncryptionKey(String masterKeyPath, String encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) throws DLLException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 2ddc4ff4a..e0524d9e6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3853,6 +3853,9 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe 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) { if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString); @@ -3861,8 +3864,78 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe break; } else if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { - fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString); + + // 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; + + byte[] accessTokenFromDLL = dllInfo.accessTokenBytes; + + String accessToken = new String(accessTokenFromDLL, UTF_16LE); + + 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); + } + + int millisecondsRemaining = TimerRemaining(timerExpire); + if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory || timerHasExpired(timerExpire) + || (sleepInterval >= millisecondsRemaining)) { + + String errorStatus = Integer.toHexString(adalException.GetStatus()); + + 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 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."); + } + + 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; + } + } + // 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; } From e79f964548d10f42d0ad41f3c52c2557a910dd81 Mon Sep 17 00:00:00 2001 From: Nikhil Sidhaye Date: Fri, 19 Jan 2018 20:52:59 -0500 Subject: [PATCH 38/39] Updated as per review comments --- .gitignore | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 32c0d6071..b8095970d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ local.properties .gradle/ .loadpath outdated-dependencies.txt +pom.xml.versionBackup # External tool builders .externalToolBuilders/ diff --git a/pom.xml b/pom.xml index 402b7f740..cebe7dca6 100644 --- a/pom.xml +++ b/pom.xml @@ -340,7 +340,7 @@ org.codehaus.mojo versions-maven-plugin - true + true outdated-dependencies.txt file:///${session.executionRootDirectory}/maven-version-rules.xml From 4fef240271dbec68ecfddb39ae20f3818502261c Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 26 Jan 2018 20:48:51 -0800 Subject: [PATCH 39/39] Update POM XML with SNAPSHOT Tag --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c0b14f530..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