Copyright Microsoft Corporation Licensed 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.
/** * Copyright Microsoft Corporation * * Licensed 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 com.microsoft.azure.storage.core; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.TimeoutException; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.xml.sax.SAXException; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.RequestOptions; import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultContinuationType; import com.microsoft.azure.storage.StorageErrorCode; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageExtendedErrorInformation;
RESERVED FOR INTERNAL USE. A class which provides utility methods.
/** * RESERVED FOR INTERNAL USE. A class which provides utility methods. */
public final class Utility {
Thread local for storing GMT date format.
/** * Thread local for storing GMT date format. */
private static ThreadLocal<DateFormat> RFC1123_GMT_DATE_TIME_FORMATTER = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { final DateFormat formatter = new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US); formatter.setTimeZone(GMT_ZONE); return formatter; } };
Stores a reference to the GMT time zone.
/** * Stores a reference to the GMT time zone. */
public static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
Stores a reference to the UTC time zone.
/** * Stores a reference to the UTC time zone. */
public static final TimeZone UTC_ZONE = TimeZone.getTimeZone("UTC");
Stores a reference to the US locale.
/** * Stores a reference to the US locale. */
public static final Locale LOCALE_US = Locale.US;
Stores a reference to the RFC1123 date/time pattern.
/** * Stores a reference to the RFC1123 date/time pattern. */
private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
Stores a reference to the ISO8601 date/time pattern.
/** * Stores a reference to the ISO8601 date/time pattern. */
private static final String ISO8601_PATTERN_NO_SECONDS = "yyyy-MM-dd'T'HH:mm'Z'";
Stores a reference to the ISO8601 date/time pattern.
/** * Stores a reference to the ISO8601 date/time pattern. */
private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
Stores a reference to the Java version of ISO8601_LONG date/time pattern. The full version cannot be used because Java Dates have millisecond precision.
/** * Stores a reference to the Java version of ISO8601_LONG date/time pattern. The full version cannot be used * because Java Dates have millisecond precision. */
private static final String JAVA_ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
List of ports used for path style addressing.
/** * List of ports used for path style addressing. */
private static final List<Integer> pathStylePorts = Arrays.asList(10000, 10001, 10002, 10003, 10004, 10100, 10101, 10102, 10103, 10104, 11000, 11001, 11002, 11003, 11004, 11100, 11101, 11102, 11103, 11104);
Used to create Json parsers and generators.
/** * Used to create Json parsers and generators. */
private static final JsonFactory jsonFactory = new JsonFactory();
Thread local for SAXParser.
/** * Thread local for SAXParser. */
private static final ThreadLocal<SAXParser> saxParserThreadLocal = new ThreadLocal<SAXParser>() { @Override public SAXParser initialValue() { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); try { return factory.newSAXParser(); } catch (SAXException e) { throw new RuntimeException("Unable to create SAXParser", e); } catch (ParserConfigurationException e) { throw new RuntimeException("Check parser configuration", e); } } };
A factory to create XMLStreamWriter instances.
/** * A factory to create XMLStreamWriter instances. */
private static final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
Stores a reference to the date/time pattern with the greatest precision Java.util.Date is capable of expressing.
/** * Stores a reference to the date/time pattern with the greatest precision Java.util.Date is capable of expressing. */
private static final String MAX_PRECISION_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
The length of a datestring that matches the MAX_PRECISION_PATTERN.
/** * The length of a datestring that matches the MAX_PRECISION_PATTERN. */
private static final int MAX_PRECISION_DATESTRING_LENGTH = MAX_PRECISION_PATTERN.replaceAll("'", "").length();
Determines the size of an input stream, and optionally calculates the MD5 hash for the stream.
Params:
  • sourceStream – A InputStream object that represents the stream to measure.
  • writeLength – The number of bytes to read from the stream.
  • abandonLength – The number of bytes to read before the analysis is abandoned. Set this value to -1 to force the entire stream to be read. This parameter is provided to support upload thresholds.
  • rewindSourceStream – true if the stream should be rewound after it is read; otherwise, false.
  • calculateMD5 – true if an MD5 hash will be calculated; otherwise, false.
Throws:
Returns:A StreamMd5AndLength object that contains the stream length, and optionally the MD5 hash.
/** * * Determines the size of an input stream, and optionally calculates the MD5 hash for the stream. * * @param sourceStream * A <code>InputStream</code> object that represents the stream to measure. * @param writeLength * The number of bytes to read from the stream. * @param abandonLength * The number of bytes to read before the analysis is abandoned. Set this value to <code>-1</code> to * force the entire stream to be read. This parameter is provided to support upload thresholds. * @param rewindSourceStream * <code>true</code> if the stream should be rewound after it is read; otherwise, <code>false</code>. * @param calculateMD5 * <code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>. * * @return A {@link StreamMd5AndLength} object that contains the stream length, and optionally the MD5 hash. * * @throws IOException * If an I/O error occurs. * @throws StorageException * If a storage service error occurred. */
public static StreamMd5AndLength analyzeStream(final InputStream sourceStream, long writeLength, long abandonLength, final boolean rewindSourceStream, final boolean calculateMD5) throws IOException, StorageException { if (abandonLength < 0) { abandonLength = Long.MAX_VALUE; } if (rewindSourceStream) { if (!sourceStream.markSupported()) { throw new IllegalArgumentException(SR.INPUT_STREAM_SHOULD_BE_MARKABLE); } sourceStream.mark(Constants.MAX_MARK_LENGTH); } MessageDigest digest = null; if (calculateMD5) { try { digest = MessageDigest.getInstance("MD5"); } catch (final NoSuchAlgorithmException e) { // This wont happen, throw fatal. throw Utility.generateNewUnexpectedStorageException(e); } } if (writeLength < 0) { writeLength = Long.MAX_VALUE; } final StreamMd5AndLength retVal = new StreamMd5AndLength(); int count = -1; final byte[] retrievedBuff = new byte[Constants.BUFFER_COPY_LENGTH]; int nextCopy = (int) Math.min(retrievedBuff.length, writeLength - retVal.getLength()); count = sourceStream.read(retrievedBuff, 0, nextCopy); while (nextCopy > 0 && count != -1) { if (calculateMD5) { digest.update(retrievedBuff, 0, count); } retVal.setLength(retVal.getLength() + count); if (retVal.getLength() > abandonLength) { // Abandon operation retVal.setLength(-1); retVal.setMd5(null); break; } nextCopy = (int) Math.min(retrievedBuff.length, writeLength - retVal.getLength()); count = sourceStream.read(retrievedBuff, 0, nextCopy); } if (retVal.getLength() != -1 && calculateMD5) { retVal.setMd5(Base64.encode(digest.digest())); } if (retVal.getLength() != -1 && writeLength > 0) { retVal.setLength(Math.min(retVal.getLength(), writeLength)); } if (rewindSourceStream) { sourceStream.reset(); sourceStream.mark(Constants.MAX_MARK_LENGTH); } return retVal; }
Encrypts an input stream up to a given length. Exits early if the encrypted data is longer than the abandon length.
Params:
  • sourceStream – A InputStream object that represents the stream to measure.
  • targetStream – A ByteArrayOutputStream object that represents the stream to write the encrypted data.
  • cipher – The Cipher to use to encrypt the data.
  • writeLength – The number of bytes to read and encrypt from the sourceStream.
  • abandonLength – The number of bytes to read before the analysis is abandoned. Set this value to -1 to force the entire stream to be read. This parameter is provided to support upload thresholds.
Throws:
Returns: The size of the encrypted stream, or -1 if the encrypted stream would be over the abandonLength.
/** * Encrypts an input stream up to a given length. * Exits early if the encrypted data is longer than the abandon length. * * @param sourceStream * A <code>InputStream</code> object that represents the stream to measure. * @param targetStream * A <code>ByteArrayOutputStream</code> object that represents the stream to write the encrypted data. * @param cipher * The <code>Cipher</code> to use to encrypt the data. * @param writeLength * The number of bytes to read and encrypt from the sourceStream. * @param abandonLength * The number of bytes to read before the analysis is abandoned. Set this value to <code>-1</code> to * force the entire stream to be read. This parameter is provided to support upload thresholds. * @return * The size of the encrypted stream, or -1 if the encrypted stream would be over the abandonLength. * @throws IOException * If an I/O error occurs. */
public static long encryptStreamIfUnderThreshold(final InputStream sourceStream, final ByteArrayOutputStream targetStream, Cipher cipher, long writeLength, long abandonLength) throws IOException { if (abandonLength < 0) { abandonLength = Long.MAX_VALUE; } if (!sourceStream.markSupported()) { throw new IllegalArgumentException(SR.INPUT_STREAM_SHOULD_BE_MARKABLE); } sourceStream.mark(Constants.MAX_MARK_LENGTH); if (writeLength < 0) { writeLength = Long.MAX_VALUE; } CipherOutputStream encryptStream = new CipherOutputStream(targetStream, cipher); int count = -1; long totalEncryptedLength = targetStream.size(); final byte[] retrievedBuff = new byte[Constants.BUFFER_COPY_LENGTH]; int nextCopy = (int) Math.min(retrievedBuff.length, writeLength - totalEncryptedLength); count = sourceStream.read(retrievedBuff, 0, nextCopy); while (nextCopy > 0 && count != -1) { // Note: We are flushing the CryptoStream on every write here. This way, we don't end up encrypting more data than we intend here, if // we go over the abandonLength. encryptStream.write(retrievedBuff, 0, count); encryptStream.flush(); totalEncryptedLength = targetStream.size(); if (totalEncryptedLength > abandonLength) { // Abandon operation break; } nextCopy = (int) Math.min(retrievedBuff.length, writeLength - totalEncryptedLength); count = sourceStream.read(retrievedBuff, 0, nextCopy); } sourceStream.reset(); sourceStream.mark(Constants.MAX_MARK_LENGTH); encryptStream.close(); totalEncryptedLength = targetStream.size(); if (totalEncryptedLength > abandonLength) { totalEncryptedLength = -1; } return totalEncryptedLength; }
Asserts a continuation token is of the specified type.
Params:
  • continuationToken – A ResultContinuation object that represents the continuation token whose type is being examined.
  • continuationType – A ResultContinuationType value that represents the continuation token type being asserted with the specified continuation token.
/** * Asserts a continuation token is of the specified type. * * @param continuationToken * A {@link ResultContinuation} object that represents the continuation token whose type is being * examined. * @param continuationType * A {@link ResultContinuationType} value that represents the continuation token type being asserted with * the specified continuation token. */
public static void assertContinuationType(final ResultContinuation continuationToken, final ResultContinuationType continuationType) { if (continuationToken != null) { if (!(continuationToken.getContinuationType() == ResultContinuationType.NONE || continuationToken .getContinuationType() == continuationType)) { final String errorMessage = String.format(Utility.LOCALE_US, SR.UNEXPECTED_CONTINUATION_TYPE, continuationType, continuationToken.getContinuationType()); throw new IllegalArgumentException(errorMessage); } } }
Asserts that a value is not null.
Params:
  • param – A String that represents the name of the parameter, which becomes the exception message text if the value parameter is null.
  • value – An Object object that represents the value of the specified parameter. This is the value being asserted as not null.
/** * Asserts that a value is not <code>null</code>. * * @param param * A <code>String</code> that represents the name of the parameter, which becomes the exception message * text if the <code>value</code> parameter is <code>null</code>. * @param value * An <code>Object</code> object that represents the value of the specified parameter. This is the value * being asserted as not <code>null</code>. */
public static void assertNotNull(final String param, final Object value) { if (value == null) { throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_NULL, param)); } }
Asserts that the specified string is not null or empty.
Params:
  • param – A String that represents the name of the parameter, which becomes the exception message text if the value parameter is null or an empty string.
  • value – A String that represents the value of the specified parameter. This is the value being asserted as not null and not an empty string.
/** * Asserts that the specified string is not <code>null</code> or empty. * * @param param * A <code>String</code> that represents the name of the parameter, which becomes the exception message * text if the <code>value</code> parameter is <code>null</code> or an empty string. * @param value * A <code>String</code> that represents the value of the specified parameter. This is the value being * asserted as not <code>null</code> and not an empty string. */
public static void assertNotNullOrEmpty(final String param, final String value) { assertNotNull(param, value); if (Utility.isNullOrEmpty(value)) { throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_NULL_OR_EMPTY, param)); } }
Asserts that the specified integer is in the valid range.
Params:
  • param – A String that represents the name of the parameter, which becomes the exception message text if the value parameter is out of bounds.
  • value – The value of the specified parameter.
  • min – The minimum value for the specified parameter.
  • max – The maximum value for the specified parameter.
/** * Asserts that the specified integer is in the valid range. * * @param param * A <code>String</code> that represents the name of the parameter, which becomes the exception message * text if the <code>value</code> parameter is out of bounds. * @param value * The value of the specified parameter. * @param min * The minimum value for the specified parameter. * @param max * The maximum value for the specified parameter. */
public static void assertInBounds(final String param, final long value, final long min, final long max) { if (value < min || value > max) { throw new IllegalArgumentException(String.format(SR.PARAMETER_NOT_IN_RANGE, param, min, max)); } }
Asserts that the specified value is greater than or equal to the min value.
Params:
  • param – A String that represents the name of the parameter, which becomes the exception message text if the value parameter is out of bounds.
  • value – The value of the specified parameter.
  • min – The minimum value for the specified parameter.
/** * Asserts that the specified value is greater than or equal to the min value. * * @param param * A <code>String</code> that represents the name of the parameter, which becomes the exception message * text if the <code>value</code> parameter is out of bounds. * @param value * The value of the specified parameter. * @param min * The minimum value for the specified parameter. */
public static void assertGreaterThanOrEqual(final String param, final long value, final long min) { if (value < min) { throw new IllegalArgumentException(String.format(SR.PARAMETER_SHOULD_BE_GREATER_OR_EQUAL, param, min)); } }
Appends 2 byte arrays.
Params:
  • arr1 – First array.
  • arr2 – Second array.
Returns:The result byte array.
/** * Appends 2 byte arrays. * @param arr1 * First array. * @param arr2 * Second array. * @return The result byte array. */
public static byte[] binaryAppend(byte[] arr1, byte[] arr2) { byte[] result = new byte[arr1.length + arr2.length]; System.arraycopy(arr1, 0, result, 0, arr1.length); System.arraycopy(arr2, 0, result, arr1.length, arr2.length); return result; }
Returns a value representing whether the maximum execution time would be surpassed.
Params:
  • operationExpiryTimeInMs – the time the request expires
Returns:true if the maximum execution time would be surpassed; otherwise, false.
/** * Returns a value representing whether the maximum execution time would be surpassed. * * @param operationExpiryTimeInMs * the time the request expires * @return <code>true</code> if the maximum execution time would be surpassed; otherwise, <code>false</code>. */
public static boolean validateMaxExecutionTimeout(Long operationExpiryTimeInMs) { return validateMaxExecutionTimeout(operationExpiryTimeInMs, 0); }
Returns a value representing whether the maximum execution time would be surpassed.
Params:
  • operationExpiryTimeInMs – the time the request expires
  • additionalInterval – any additional time required from now
Returns:true if the maximum execution time would be surpassed; otherwise, false.
/** * Returns a value representing whether the maximum execution time would be surpassed. * * @param operationExpiryTimeInMs * the time the request expires * @param additionalInterval * any additional time required from now * @return <code>true</code> if the maximum execution time would be surpassed; otherwise, <code>false</code>. */
public static boolean validateMaxExecutionTimeout(Long operationExpiryTimeInMs, long additionalInterval) { if (operationExpiryTimeInMs != null) { long currentTime = new Date().getTime(); return operationExpiryTimeInMs < currentTime + additionalInterval; } return false; }
Returns a value representing the remaining time before the operation expires.
Params:
  • operationExpiryTimeInMs – the time the request expires
  • timeoutIntervalInMs – the server side timeout interval
Throws:
  • StorageException – wraps a TimeoutException if there is no more time remaining
Returns:the remaining time before the operation expires
/** * Returns a value representing the remaining time before the operation expires. * * @param operationExpiryTimeInMs * the time the request expires * @param timeoutIntervalInMs * the server side timeout interval * @return the remaining time before the operation expires * @throws StorageException * wraps a TimeoutException if there is no more time remaining */
public static int getRemainingTimeout(Long operationExpiryTimeInMs, Integer timeoutIntervalInMs) throws StorageException { if (operationExpiryTimeInMs != null) { long remainingTime = operationExpiryTimeInMs - new Date().getTime(); if (remainingTime > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (remainingTime > 0) { return (int) remainingTime; } else { TimeoutException timeoutException = new TimeoutException(SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION); StorageException translatedException = new StorageException( StorageErrorCodeStrings.OPERATION_TIMED_OUT, SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION, Constants.HeaderConstants.HTTP_UNUSED_306, null, timeoutException); throw translatedException; } } else if (timeoutIntervalInMs != null) { return timeoutIntervalInMs + Constants.DEFAULT_READ_TIMEOUT; } else { return Constants.DEFAULT_READ_TIMEOUT; } }
Returns a value that indicates whether a specified URI is a path-style URI.
Params:
  • baseURI – A java.net.URI value that represents the URI being checked.
Returns:true if the specified URI is path-style; otherwise, false.
/** * Returns a value that indicates whether a specified URI is a path-style URI. * * @param baseURI * A <code>java.net.URI</code> value that represents the URI being checked. * @return <code>true</code> if the specified URI is path-style; otherwise, <code>false</code>. */
public static boolean determinePathStyleFromUri(final URI baseURI) { String path = baseURI.getPath(); if (path != null && path.startsWith("/")) { path = path.substring(1); } // if the path is null or empty, this is not path-style if (Utility.isNullOrEmpty(path)) { return false; } // if this contains a port or has a host which is not DNS, this is path-style return pathStylePorts.contains(baseURI.getPort()) || !isHostDnsName(baseURI); }
Returns a boolean indicating whether the host of the specified URI is DNS.
Params:
  • uri – The URI whose host to evaluate.
Returns:true if the host is DNS; otherwise, false.
/** * Returns a boolean indicating whether the host of the specified URI is DNS. * * @param uri * The URI whose host to evaluate. * @return <code>true</code> if the host is DNS; otherwise, <code>false</code>. */
private static boolean isHostDnsName(URI uri) { String host = uri.getHost(); for (int i = 0; i < host.length(); i++) { char hostChar = host.charAt(i); if (!Character.isDigit(hostChar) && !(hostChar == '.')) { return true; } } return false; }
Reads character data for the Etag element from an XML stream reader.
Returns:A String that represents the character data for the Etag element.
/** * Reads character data for the Etag element from an XML stream reader. * * @return A <code>String</code> that represents the character data for the Etag element. */
public static String formatETag(final String etag) { if (etag.startsWith("\"") && etag.endsWith("\"")) { return etag; } else { return String.format("\"%s\"", etag); } }
Returns an unexpected storage exception.
Params:
  • cause – An Exception object that represents the initial exception that caused the unexpected error.
Returns:A StorageException object that represents the unexpected storage exception being thrown.
/** * Returns an unexpected storage exception. * * @param cause * An <code>Exception</code> object that represents the initial exception that caused the unexpected * error. * * @return A {@link StorageException} object that represents the unexpected storage exception being thrown. */
public static StorageException generateNewUnexpectedStorageException(final Exception cause) { final StorageException exceptionRef = new StorageException(StorageErrorCode.NONE.toString(), "Unexpected internal storage client error.", 306, // unused null, null); exceptionRef.initCause(cause); return exceptionRef; }
Returns the current GMT date/time String using the RFC1123 pattern.
Returns:A String that represents the current GMT date/time using the RFC1123 pattern.
/** * Returns the current GMT date/time String using the RFC1123 pattern. * * @return A <code>String</code> that represents the current GMT date/time using the RFC1123 pattern. */
public static String getGMTTime() { return getGMTTime(new Date()); }
Returns the GTM date/time String for the specified value using the RFC1123 pattern.
Params:
  • date – A Date object that represents the date to convert to GMT date/time in the RFC1123 pattern.
Returns:A String that represents the GMT date/time for the specified value using the RFC1123 pattern.
/** * Returns the GTM date/time String for the specified value using the RFC1123 pattern. * * @param date * A <code>Date</code> object that represents the date to convert to GMT date/time in the RFC1123 * pattern. * * @return A <code>String</code> that represents the GMT date/time for the specified value using the RFC1123 * pattern. */
public static String getGMTTime(final Date date) { return RFC1123_GMT_DATE_TIME_FORMATTER.get().format(date); }
Returns the UTC date/time String for the specified value using Java's version of the ISO8601 pattern, which is limited to millisecond precision.
Params:
  • date – A Date object that represents the date to convert to UTC date/time in Java's version of the ISO8601 pattern.
Returns:A String that represents the UTC date/time for the specified value using Java's version of the ISO8601 pattern.
/** * Returns the UTC date/time String for the specified value using Java's version of the ISO8601 pattern, * which is limited to millisecond precision. * * @param date * A <code>Date</code> object that represents the date to convert to UTC date/time in Java's version * of the ISO8601 pattern. * * @return A <code>String</code> that represents the UTC date/time for the specified value using Java's version * of the ISO8601 pattern. */
public static String getJavaISO8601Time(Date date) { final DateFormat formatter = new SimpleDateFormat(JAVA_ISO8601_PATTERN, LOCALE_US); formatter.setTimeZone(UTC_ZONE); return formatter.format(date); }
Returns a JsonGenerator with the specified StringWriter.
Params:
  • strWriter – The StringWriter to use to create the JsonGenerator instance.
Throws:
Returns:A JsonGenerator instance
/** * Returns a <code>JsonGenerator</code> with the specified <code>StringWriter</code>. * * @param strWriter * The <code>StringWriter</code> to use to create the <code>JsonGenerator</code> instance. * @return A <code>JsonGenerator</code> instance * * @throws IOException */
public static JsonGenerator getJsonGenerator(StringWriter strWriter) throws IOException { return jsonFactory.createGenerator(strWriter); }
Returns a JsonGenerator with the specified OutputStream.
Params:
  • outStream – The OutputStream to use to create the JsonGenerator instance.
Throws:
Returns:A JsonGenerator instance
/** * Returns a <code>JsonGenerator</code> with the specified <code>OutputStream</code>. * * @param outStream * The <code>OutputStream</code> to use to create the <code>JsonGenerator</code> instance. * @return A <code>JsonGenerator</code> instance * * @throws IOException */
public static JsonGenerator getJsonGenerator(OutputStream outStream) throws IOException { return jsonFactory.createGenerator(outStream); }
Returns a JsonParser with the specified String. This JsonParser will allow non-numeric numbers.
Params:
  • jsonString – The String to use to create the JsonGenerator instance.
Throws:
Returns:A JsonGenerator instance.
/** * Returns a <code>JsonParser</code> with the specified <code>String</code>. This JsonParser * will allow non-numeric numbers. * * @param jsonString * The <code>String</code> to use to create the <code>JsonGenerator</code> instance. * @return A <code>JsonGenerator</code> instance. * * @throws IOException */
public static JsonParser getJsonParser(final String jsonString) throws JsonParseException, IOException { JsonParser parser = jsonFactory.createParser(jsonString); return setupJsonParser(parser); }
Returns a JsonParser with the specified InputStream. This JsonParser will allow non-numeric numbers.
Params:
  • inStream – The InputStream to use to create the JsonGenerator instance.
Throws:
Returns:A JsonGenerator instance.
/** * Returns a <code>JsonParser</code> with the specified <code>InputStream</code>. This JsonParser * will allow non-numeric numbers. * * @param inStream * The <code>InputStream</code> to use to create the <code>JsonGenerator</code> instance. * @return A <code>JsonGenerator</code> instance. * * @throws IOException */
public static JsonParser getJsonParser(final InputStream inStream) throws JsonParseException, IOException { JsonParser parser = jsonFactory.createParser(inStream); return setupJsonParser(parser); }
Returns a JsonParser This JsonParser will allow non-numeric numbers.
Params:
  • parser – A JsonParser to setup.
Returns: A JsonParser with settings configured.
/** * Returns a <code>JsonParser</code> This JsonParser will allow non-numeric numbers. * @param parser * A <code>JsonParser</code> to setup. * @return * A <code>JsonParser</code> with settings configured. */
private static JsonParser setupJsonParser(final JsonParser parser) { // IMPORTANT: DO NOT REMOVE! // don't close the stream and allow it to be drained completely in ExecutionEngine to improve socket reuse parser.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); // allows handling of infinity, -infinity, and NaN for Doubles return parser.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS); }
Returns a namespace aware SAXParser.
Throws:
Returns:A SAXParser instance which is namespace aware
/** * Returns a namespace aware <code>SAXParser</code>. * * @return A <code>SAXParser</code> instance which is namespace aware * * @throws ParserConfigurationException * @throws SAXException */
public static SAXParser getSAXParser() throws ParserConfigurationException, SAXException { SAXParser parser = saxParserThreadLocal.get(); parser.reset(); //reset to original config return parser; }
Returns the standard header value from the specified connection request, or an empty string if no header value has been specified for the request.
Params:
  • conn – An HttpURLConnection object that represents the request.
  • headerName – A String that represents the name of the header being requested.
Returns:A String that represents the header value, or null if there is no corresponding header value for headerName.
/** * Returns the standard header value from the specified connection request, or an empty string if no header value * has been specified for the request. * * @param conn * An <code>HttpURLConnection</code> object that represents the request. * @param headerName * A <code>String</code> that represents the name of the header being requested. * * @return A <code>String</code> that represents the header value, or <code>null</code> if there is no corresponding * header value for <code>headerName</code>. */
public static String getStandardHeaderValue(final HttpURLConnection conn, final String headerName) { final String headerValue = conn.getRequestProperty(headerName); // Coalesce null value return headerValue == null ? Constants.EMPTY_STRING : headerValue; }
Returns the UTC date/time for the specified value using the ISO8601 pattern.
Params:
  • value – A Date object that represents the date to convert to UTC date/time in the ISO8601 pattern. If this value is null, this method returns an empty string.
Returns:A String that represents the UTC date/time for the specified value using the ISO8601 pattern, or an empty string if value is null.
/** * Returns the UTC date/time for the specified value using the ISO8601 pattern. * * @param value * A <code>Date</code> object that represents the date to convert to UTC date/time in the ISO8601 * pattern. If this value is <code>null</code>, this method returns an empty string. * * @return A <code>String</code> that represents the UTC date/time for the specified value using the ISO8601 * pattern, or an empty string if <code>value</code> is <code>null</code>. */
public static String getUTCTimeOrEmpty(final Date value) { if (value == null) { return Constants.EMPTY_STRING; } final DateFormat iso8601Format = new SimpleDateFormat(ISO8601_PATTERN, LOCALE_US); iso8601Format.setTimeZone(UTC_ZONE); return iso8601Format.format(value); }
Returns a XMLStreamWriter with the specified StringWriter.
Params:
  • outWriter – The StringWriter to use to create the XMLStreamWriter instance.
Throws:
Returns:A XMLStreamWriter instance
/** * Returns a <code>XMLStreamWriter</code> with the specified <code>StringWriter</code>. * * @param outWriter * The <code>StringWriter</code> to use to create the <code>XMLStreamWriter</code> instance. * @return A <code>XMLStreamWriter</code> instance * * @throws XMLStreamException */
public static XMLStreamWriter createXMLStreamWriter(StringWriter outWriter) throws XMLStreamException { return xmlOutputFactory.createXMLStreamWriter(outWriter); }
Creates an instance of the IOException class using the specified exception.
Params:
  • ex – An Exception object that represents the exception used to create the IO exception.
Returns:A java.io.IOException object that represents the created IO exception.
/** * Creates an instance of the <code>IOException</code> class using the specified exception. * * @param ex * An <code>Exception</code> object that represents the exception used to create the IO exception. * * @return A <code>java.io.IOException</code> object that represents the created IO exception. */
public static IOException initIOException(final Exception ex) { String message = null; if (ex != null && ex.getMessage() != null) { message = ex.getMessage() + " Please see the cause for further information."; } else { message = "Please see the cause for further information."; } final IOException retEx = new IOException(message, ex); return retEx; }
Returns a value that indicates whether the specified string is null or empty.
Params:
  • value – A String being examined for null or empty.
Returns:true if the specified value is null or empty; otherwise, false
/** * Returns a value that indicates whether the specified string is <code>null</code> or empty. * * @param value * A <code>String</code> being examined for <code>null</code> or empty. * * @return <code>true</code> if the specified value is <code>null</code> or empty; otherwise, <code>false</code> */
public static boolean isNullOrEmpty(final String value) { return value == null || value.length() == 0; }
Returns a value that indicates whether the specified string is null, empty, or whitespace.
Params:
  • value – A String being examined for null, empty, or whitespace.
Returns:true if the specified value is null, empty, or whitespace; otherwise, false
/** * Returns a value that indicates whether the specified string is <code>null</code>, empty, or whitespace. * * @param value * A <code>String</code> being examined for <code>null</code>, empty, or whitespace. * * @return <code>true</code> if the specified value is <code>null</code>, empty, or whitespace; otherwise, * <code>false</code> */
public static boolean isNullOrEmptyOrWhitespace(final String value) { return value == null || value.trim().length() == 0; }
Parses a connection string and returns its values as a hash map of key/value pairs.
Params:
  • parseString – A String that represents the connection string to parse.
Returns:A java.util.HashMap object that represents the hash map of the key / value pairs parsed from the connection string.
/** * Parses a connection string and returns its values as a hash map of key/value pairs. * * @param parseString * A <code>String</code> that represents the connection string to parse. * * @return A <code>java.util.HashMap</code> object that represents the hash map of the key / value pairs parsed from * the connection string. */
public static HashMap<String, String> parseAccountString(final String parseString) { // 1. split name value pairs by splitting on the ';' character final String[] valuePairs = parseString.split(";"); final HashMap<String, String> retVals = new HashMap<String, String>(); // 2. for each field value pair parse into appropriate map entries for (int m = 0; m < valuePairs.length; m++) { if (valuePairs[m].length() == 0) { continue; } final int equalDex = valuePairs[m].indexOf("="); if (equalDex < 1) { throw new IllegalArgumentException(SR.INVALID_CONNECTION_STRING); } final String key = valuePairs[m].substring(0, equalDex); final String value = valuePairs[m].substring(equalDex + 1); // 2.1 add to map retVals.put(key, value); } return retVals; }
Returns a GMT date for the specified string in the RFC1123 pattern.
Params:
  • value – A String that represents the string to parse.
Throws:
Returns:A Date object that represents the GMT date in the RFC1123 pattern.
/** * Returns a GMT date for the specified string in the RFC1123 pattern. * * @param value * A <code>String</code> that represents the string to parse. * * @return A <code>Date</code> object that represents the GMT date in the RFC1123 pattern. * * @throws ParseException * If the specified string is invalid. */
public static Date parseRFC1123DateFromStringInGMT(final String value) throws ParseException { return RFC1123_GMT_DATE_TIME_FORMATTER.get().parse(value); }
Performs safe decoding of the specified string, taking care to preserve each + character, rather than replacing it with a space character.
Params:
  • stringToDecode – A String that represents the string to decode.
Throws:
Returns:A String that represents the decoded string.
/** * Performs safe decoding of the specified string, taking care to preserve each <code>+</code> character, rather * than replacing it with a space character. * * @param stringToDecode * A <code>String</code> that represents the string to decode. * * @return A <code>String</code> that represents the decoded string. * * @throws StorageException * If a storage service error occurred. */
public static String safeDecode(final String stringToDecode) throws StorageException { if (stringToDecode == null) { return null; } if (stringToDecode.length() == 0) { return Constants.EMPTY_STRING; } try { if (stringToDecode.contains("+")) { final StringBuilder outBuilder = new StringBuilder(); int startDex = 0; for (int m = 0; m < stringToDecode.length(); m++) { if (stringToDecode.charAt(m) == '+') { if (m > startDex) { outBuilder.append(URLDecoder.decode(stringToDecode.substring(startDex, m), Constants.UTF8_CHARSET)); } outBuilder.append("+"); startDex = m + 1; } } if (startDex != stringToDecode.length()) { outBuilder.append(URLDecoder.decode(stringToDecode.substring(startDex, stringToDecode.length()), Constants.UTF8_CHARSET)); } return outBuilder.toString(); } else { return URLDecoder.decode(stringToDecode, Constants.UTF8_CHARSET); } } catch (final UnsupportedEncodingException e) { throw Utility.generateNewUnexpectedStorageException(e); } }
Performs safe encoding of the specified string, taking care to insert %20 for each space character, instead of inserting the + character.
Params:
  • stringToEncode – A String that represents the string to encode.
Throws:
Returns:A String that represents the encoded string.
/** * Performs safe encoding of the specified string, taking care to insert <code>%20</code> for each space character, * instead of inserting the <code>+</code> character. * * @param stringToEncode * A <code>String</code> that represents the string to encode. * * @return A <code>String</code> that represents the encoded string. * * @throws StorageException * If a storage service error occurred. */
public static String safeEncode(final String stringToEncode) throws StorageException { if (stringToEncode == null) { return null; } if (stringToEncode.length() == 0) { return Constants.EMPTY_STRING; } try { final String tString = URLEncoder.encode(stringToEncode, Constants.UTF8_CHARSET); if (stringToEncode.contains(" ")) { final StringBuilder outBuilder = new StringBuilder(); int startDex = 0; for (int m = 0; m < stringToEncode.length(); m++) { if (stringToEncode.charAt(m) == ' ') { if (m > startDex) { outBuilder.append(URLEncoder.encode(stringToEncode.substring(startDex, m), Constants.UTF8_CHARSET)); } outBuilder.append("%20"); startDex = m + 1; } } if (startDex != stringToEncode.length()) { outBuilder.append(URLEncoder.encode(stringToEncode.substring(startDex, stringToEncode.length()), Constants.UTF8_CHARSET)); } return outBuilder.toString(); } else { return tString; } } catch (final UnsupportedEncodingException e) { throw Utility.generateNewUnexpectedStorageException(e); } }
Determines the relative difference between the two specified URIs.
Params:
  • baseURI – A java.net.URI object that represents the base URI for which toUri will be made relative.
  • toUri – A java.net.URI object that represents the URI to make relative to baseURI.
Throws:
Returns:A String that either represents the relative URI of toUri to baseURI, or the URI of toUri itself, depending on whether the hostname and scheme are identical for toUri and baseURI. If the hostname and scheme of baseURI and toUri are identical, this method returns an unencoded relative URI such that if appended to baseURI, it will yield toUri. If the hostname or scheme of baseURI and toUri are not identical, this method returns an unencoded full URI specified by toUri.
/** * Determines the relative difference between the two specified URIs. * * @param baseURI * A <code>java.net.URI</code> object that represents the base URI for which <code>toUri</code> will be * made relative. * @param toUri * A <code>java.net.URI</code> object that represents the URI to make relative to <code>baseURI</code>. * * @return A <code>String</code> that either represents the relative URI of <code>toUri</code> to * <code>baseURI</code>, or the URI of <code>toUri</code> itself, depending on whether the hostname and * scheme are identical for <code>toUri</code> and <code>baseURI</code>. If the hostname and scheme of * <code>baseURI</code> and <code>toUri</code> are identical, this method returns an unencoded relative URI * such that if appended to <code>baseURI</code>, it will yield <code>toUri</code>. If the hostname or * scheme of <code>baseURI</code> and <code>toUri</code> are not identical, this method returns an unencoded * full URI specified by <code>toUri</code>. * * @throws URISyntaxException * If <code>baseURI</code> or <code>toUri</code> is invalid. */
public static String safeRelativize(final URI baseURI, final URI toUri) throws URISyntaxException { // For compatibility followed // http://msdn.microsoft.com/en-us/library/system.uri.makerelativeuri.aspx // if host and scheme are not identical return from uri if (!baseURI.getHost().equals(toUri.getHost()) || !baseURI.getScheme().equals(toUri.getScheme())) { return toUri.toString(); } final String basePath = baseURI.getPath(); String toPath = toUri.getPath(); int truncatePtr = 1; // Seek to first Difference // int maxLength = Math.min(basePath.length(), toPath.length()); int m = 0; int ellipsesCount = 0; for (; m < basePath.length(); m++) { if (m >= toPath.length()) { if (basePath.charAt(m) == '/') { ellipsesCount++; } } else { if (basePath.charAt(m) != toPath.charAt(m)) { break; } else if (basePath.charAt(m) == '/') { truncatePtr = m + 1; } } } // ../containername and ../containername/{path} should increment the truncatePtr // otherwise toPath will incorrectly begin with /containername if (m < toPath.length() && toPath.charAt(m) == '/') { // this is to handle the empty directory case with the '/' delimiter // for example, ../containername/ and ../containername// should not increment the truncatePtr if (!(toPath.charAt(m - 1) == '/' && basePath.charAt(m - 1) == '/')) { truncatePtr = m + 1; } } if (m == toPath.length()) { // No path difference, return query + fragment return new URI(null, null, null, toUri.getQuery(), toUri.getFragment()).toString(); } else { toPath = toPath.substring(truncatePtr); final StringBuilder sb = new StringBuilder(); while (ellipsesCount > 0) { sb.append("../"); ellipsesCount--; } if (!Utility.isNullOrEmpty(toPath)) { sb.append(toPath); } if (!Utility.isNullOrEmpty(toUri.getQuery())) { sb.append("?"); sb.append(toUri.getQuery()); } if (!Utility.isNullOrEmpty(toUri.getFragment())) { sb.append("#"); sb.append(toUri.getRawFragment()); } return sb.toString(); } }
Serializes the parsed StorageException. If an exception is encountered, returns empty string.
Params:
  • ex – The StorageException to serialize.
  • opContext – The operation context which provides the logger.
/** * Serializes the parsed StorageException. If an exception is encountered, returns empty string. * * @param ex * The StorageException to serialize. * @param opContext * The operation context which provides the logger. */
public static void logHttpError(StorageException ex, OperationContext opContext) { if (Logger.shouldLog(opContext)) { try { StringBuilder bld = new StringBuilder(); bld.append("Error response received. "); bld.append("HttpStatusCode= "); bld.append(ex.getHttpStatusCode()); bld.append(", HttpStatusMessage= "); bld.append(ex.getMessage()); bld.append(", ErrorCode= "); bld.append(ex.getErrorCode()); StorageExtendedErrorInformation extendedError = ex.getExtendedErrorInformation(); if (extendedError != null) { bld.append(", ExtendedErrorInformation= {ErrorMessage= "); bld.append(extendedError.getErrorMessage()); HashMap<String, String[]> details = extendedError.getAdditionalDetails(); if (details != null) { bld.append(", AdditionalDetails= { "); for (Entry<String, String[]> detail : details.entrySet()) { bld.append(detail.getKey()); bld.append("= "); for (String value : detail.getValue()) { bld.append(value); } bld.append(","); } bld.setCharAt(bld.length() - 1, '}'); } bld.append("}"); } Logger.debug(opContext, bld.toString()); } catch (Exception e) { // Do nothing } } }
Logs the HttpURLConnection request. If an exception is encountered, logs nothing.
Params:
  • conn – The HttpURLConnection to serialize.
  • opContext – The operation context which provides the logger.
/** * Logs the HttpURLConnection request. If an exception is encountered, logs nothing. * * @param conn * The HttpURLConnection to serialize. * @param opContext * The operation context which provides the logger. */
public static void logHttpRequest(HttpURLConnection conn, OperationContext opContext) throws IOException { if (Logger.shouldLog(opContext)) { try { StringBuilder bld = new StringBuilder(); bld.append(conn.getRequestMethod()); bld.append(" "); bld.append(conn.getURL()); bld.append("\n"); // The Authorization header will not appear due to a security feature in HttpURLConnection for (Map.Entry<String, List<String>> header : conn.getRequestProperties().entrySet()) { if (header.getKey() != null) { bld.append(header.getKey()); bld.append(": "); } for (int i = 0; i < header.getValue().size(); i++) { bld.append(header.getValue().get(i)); if (i < header.getValue().size() - 1) { bld.append(","); } } bld.append('\n'); } Logger.trace(opContext, bld.toString()); } catch (Exception e) { // Do nothing } } }
Logs the HttpURLConnection response. If an exception is encountered, logs nothing.
Params:
  • conn – The HttpURLConnection to serialize.
  • opContext – The operation context which provides the logger.
/** * Logs the HttpURLConnection response. If an exception is encountered, logs nothing. * * @param conn * The HttpURLConnection to serialize. * @param opContext * The operation context which provides the logger. */
public static void logHttpResponse(HttpURLConnection conn, OperationContext opContext) throws IOException { if (Logger.shouldLog(opContext)) { try { StringBuilder bld = new StringBuilder(); // This map's null key will contain the response code and message for (Map.Entry<String, List<String>> header : conn.getHeaderFields().entrySet()) { if (header.getKey() != null) { bld.append(header.getKey()); bld.append(": "); } for (int i = 0; i < header.getValue().size(); i++) { bld.append(header.getValue().get(i)); if (i < header.getValue().size() - 1) { bld.append(","); } } bld.append('\n'); } Logger.trace(opContext, bld.toString()); } catch (Exception e) { // Do nothing } } }
Trims the specified character from the end of a string.
Params:
  • value – A String that represents the string to trim.
  • trimChar – The character to trim from the end of the string.
Returns:The string with the specified character trimmed from the end.
/** * Trims the specified character from the end of a string. * * @param value * A <code>String</code> that represents the string to trim. * @param trimChar * The character to trim from the end of the string. * * @return The string with the specified character trimmed from the end. */
protected static String trimEnd(final String value, final char trimChar) { int stopDex = value.length() - 1; while (stopDex > 0 && value.charAt(stopDex) == trimChar) { stopDex--; } return stopDex == value.length() - 1 ? value : value.substring(stopDex); }
Trims whitespace from the beginning of a string.
Params:
  • value – A String that represents the string to trim.
Returns:The string with whitespace trimmed from the beginning.
/** * Trims whitespace from the beginning of a string. * * @param value * A <code>String</code> that represents the string to trim. * * @return The string with whitespace trimmed from the beginning. */
public static String trimStart(final String value) { int spaceDex = 0; while (spaceDex < value.length() && value.charAt(spaceDex) == ' ') { spaceDex++; } return value.substring(spaceDex); }
Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and optionally calculates the MD5 hash for the data.
Params:
  • sourceStream – An InputStream object that represents the input stream to use as the source.
  • outStream – An OutputStream object that represents the output stream to use as the destination.
  • writeLength – The number of bytes to read from the stream.
  • rewindSourceStream – true if the input stream should be rewound before it is read; otherwise, false
  • calculateMD5 – true if an MD5 hash will be calculated; otherwise, false.
  • opContext – An OperationContext object that represents the context for the current operation. This object is used to track requests to the storage service, and to provide additional runtime information about the operation.
  • options – A RequestOptions object that specifies any additional options for the request. Namely, the maximum execution time.
Throws:
Returns:A StreamMd5AndLength object that contains the output stream length, and optionally the MD5 hash.
/** * Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and * optionally calculates the MD5 hash for the data. * * @param sourceStream * An <code>InputStream</code> object that represents the input stream to use as the source. * @param outStream * An <code>OutputStream</code> object that represents the output stream to use as the destination. * @param writeLength * The number of bytes to read from the stream. * @param rewindSourceStream * <code>true</code> if the input stream should be rewound <strong>before</strong> it is read; otherwise, * <code>false</code> * @param calculateMD5 * <code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>. * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * @param options * A {@link RequestOptions} object that specifies any additional options for the request. Namely, the * maximum execution time. * @return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash. * * @throws IOException * If an I/O error occurs. * @throws StorageException * If a storage service error occurred. */
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream, long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext, final RequestOptions options) throws IOException, StorageException { return writeToOutputStream(sourceStream, outStream, writeLength, rewindSourceStream, calculateMD5, opContext, options, true); }
Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and optionally calculates the MD5 hash for the data.
Params:
  • sourceStream – An InputStream object that represents the input stream to use as the source.
  • outStream – An OutputStream object that represents the output stream to use as the destination.
  • writeLength – The number of bytes to read from the stream.
  • rewindSourceStream – true if the input stream should be rewound before it is read; otherwise, false
  • calculateMD5 – true if an MD5 hash will be calculated; otherwise, false.
  • opContext – An OperationContext object that represents the context for the current operation. This object is used to track requests to the storage service, and to provide additional runtime information about the operation.
  • options – A RequestOptions object that specifies any additional options for the request. Namely, the maximum execution time.
Throws:
Returns:A StreamMd5AndLength object that contains the output stream length, and optionally the MD5 hash.
/** * Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and * optionally calculates the MD5 hash for the data. * * @param sourceStream * An <code>InputStream</code> object that represents the input stream to use as the source. * @param outStream * An <code>OutputStream</code> object that represents the output stream to use as the destination. * @param writeLength * The number of bytes to read from the stream. * @param rewindSourceStream * <code>true</code> if the input stream should be rewound <strong>before</strong> it is read; otherwise, * <code>false</code> * @param calculateMD5 * <code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>. * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * @param options * A {@link RequestOptions} object that specifies any additional options for the request. Namely, the * maximum execution time. * @return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash. * * @throws IOException * If an I/O error occurs. * @throws StorageException * If a storage service error occurred. */
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream, long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext, final RequestOptions options, final Boolean shouldFlush) throws IOException, StorageException { return writeToOutputStream(sourceStream, outStream, writeLength, rewindSourceStream, calculateMD5, opContext, options, shouldFlush, null /*StorageRequest*/, null /* descriptor */); }
Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and optionally calculates the MD5 hash for the data.
Params:
  • sourceStream – An InputStream object that represents the input stream to use as the source.
  • outStream – An OutputStream object that represents the output stream to use as the destination.
  • writeLength – The number of bytes to read from the stream.
  • rewindSourceStream – true if the input stream should be rewound before it is read; otherwise, false
  • calculateMD5 – true if an MD5 hash will be calculated; otherwise, false.
  • opContext – An OperationContext object that represents the context for the current operation. This object is used to track requests to the storage service, and to provide additional runtime information about the operation.
  • options – A RequestOptions object that specifies any additional options for the request. Namely, the maximum execution time.
  • request – Used by download resume to set currentRequestByteCount on the request. Otherwise, null is always used.
  • descriptor – A StreamMd5AndLength object to append to in the case of recovery action or null if this is not called from a recovery. This value needs to be passed for recovery in case part of the body has already been read, the recovery will attempt to download the remaining bytes but will do MD5 validation on the originally requested range size.
Throws:
Returns:A StreamMd5AndLength object that contains the output stream length, and optionally the MD5 hash.
/** * Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and * optionally calculates the MD5 hash for the data. * * @param sourceStream * An <code>InputStream</code> object that represents the input stream to use as the source. * @param outStream * An <code>OutputStream</code> object that represents the output stream to use as the destination. * @param writeLength * The number of bytes to read from the stream. * @param rewindSourceStream * <code>true</code> if the input stream should be rewound <strong>before</strong> it is read; otherwise, * <code>false</code> * @param calculateMD5 * <code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>. * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * @param options * A {@link RequestOptions} object that specifies any additional options for the request. Namely, the * maximum execution time. * @param request * Used by download resume to set currentRequestByteCount on the request. Otherwise, null is always used. * @param descriptor * A {@link StreamMd5AndLength} object to append to in the case of recovery action or null if this is not called * from a recovery. This value needs to be passed for recovery in case part of the body has already been read, * the recovery will attempt to download the remaining bytes but will do MD5 validation on the originally * requested range size. * @return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash. * * @throws IOException * If an I/O error occurs. * @throws StorageException * If a storage service error occurred. */
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream, long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext, final RequestOptions options, final Boolean shouldFlush, StorageRequest<?, ?, Integer> request, StreamMd5AndLength descriptor) throws IOException, StorageException { if (rewindSourceStream && sourceStream.markSupported()) { sourceStream.reset(); sourceStream.mark(Constants.MAX_MARK_LENGTH); } if (descriptor == null) { descriptor = new StreamMd5AndLength(); if (calculateMD5) { try { descriptor.setDigest(MessageDigest.getInstance("MD5")); } catch (final NoSuchAlgorithmException e) { // This wont happen, throw fatal. throw Utility.generateNewUnexpectedStorageException(e); } } } else { descriptor.setMd5(null); } if (writeLength < 0) { writeLength = Long.MAX_VALUE; } final byte[] retrievedBuff = new byte[Constants.BUFFER_COPY_LENGTH]; int nextCopy = (int) Math.min(retrievedBuff.length, writeLength); int count = sourceStream.read(retrievedBuff, 0, nextCopy); while (nextCopy > 0 && count != -1) { // if maximum execution time would be exceeded if (Utility.validateMaxExecutionTimeout(options.getOperationExpiryTimeInMs())) { // throw an exception TimeoutException timeoutException = new TimeoutException(SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION); throw Utility.initIOException(timeoutException); } if (outStream != null) { outStream.write(retrievedBuff, 0, count); } if (calculateMD5) { descriptor.getDigest().update(retrievedBuff, 0, count); } descriptor.setLength(descriptor.getLength() + count); descriptor.setCurrentOperationByteCount(descriptor.getCurrentOperationByteCount() + count); if (request != null) { request.setCurrentRequestByteCount(request.getCurrentRequestByteCount() + count); request.setCurrentDescriptor(descriptor); } nextCopy = (int) Math.min(retrievedBuff.length, writeLength - descriptor.getLength()); count = sourceStream.read(retrievedBuff, 0, nextCopy); } if (outStream != null && shouldFlush) { outStream.flush(); } return descriptor; }
Private Default Constructor.
/** * Private Default Constructor. */
private Utility() { // No op } public static void checkNullaryCtor(Class<?> clazzType) { Constructor<?> ctor = null; try { ctor = clazzType.getDeclaredConstructor((Class<?>[]) null); } catch (Exception e) { throw new IllegalArgumentException(SR.MISSING_NULLARY_CONSTRUCTOR); } if (ctor == null) { throw new IllegalArgumentException(SR.MISSING_NULLARY_CONSTRUCTOR); } }
Given a String representing a date in a form of the ISO8601 pattern, generates a Date representing it with up to millisecond precision.
Params:
  • dateString – the String to be interpreted as a Date
Returns:the corresponding Date object
/** * Given a String representing a date in a form of the ISO8601 pattern, generates a Date representing it * with up to millisecond precision. * * @param dateString * the <code>String</code> to be interpreted as a <code>Date</code> * * @return the corresponding <code>Date</code> object */
public static Date parseDate(String dateString) { String pattern = MAX_PRECISION_PATTERN; switch(dateString.length()) { case 28: // "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'"-> [2012-01-04T23:21:59.1234567Z] length = 28 case 27: // "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"-> [2012-01-04T23:21:59.123456Z] length = 27 case 26: // "yyyy-MM-dd'T'HH:mm:ss.SSSSS'Z'"-> [2012-01-04T23:21:59.12345Z] length = 26 case 25: // "yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'"-> [2012-01-04T23:21:59.1234Z] length = 25 case 24: // "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"-> [2012-01-04T23:21:59.123Z] length = 24 dateString = dateString.substring(0, MAX_PRECISION_DATESTRING_LENGTH); break; case 23: // "yyyy-MM-dd'T'HH:mm:ss.SS'Z'"-> [2012-01-04T23:21:59.12Z] length = 23 // SS is assumed to be milliseconds, so a trailing 0 is necessary dateString = dateString.replace("Z", "0"); break; case 22: // "yyyy-MM-dd'T'HH:mm:ss.S'Z'"-> [2012-01-04T23:21:59.1Z] length = 22 // S is assumed to be milliseconds, so trailing 0's are necessary dateString = dateString.replace("Z", "00"); break; case 20: // "yyyy-MM-dd'T'HH:mm:ss'Z'"-> [2012-01-04T23:21:59Z] length = 20 pattern = Utility.ISO8601_PATTERN; break; case 17: // "yyyy-MM-dd'T'HH:mm'Z'"-> [2012-01-04T23:21Z] length = 17 pattern = Utility.ISO8601_PATTERN_NO_SECONDS; break; default: throw new IllegalArgumentException(String.format(SR.INVALID_DATE_STRING, dateString)); } final DateFormat format = new SimpleDateFormat(pattern, Utility.LOCALE_US); format.setTimeZone(UTC_ZONE); try { return format.parse(dateString); } catch (final ParseException e) { throw new IllegalArgumentException(String.format(SR.INVALID_DATE_STRING, dateString), e); } }
Given a String representing a date in a form of the ISO8601 pattern, generates a Date representing it with up to millisecond precision. Use parseDate(String) instead unless dateBackwardCompatibility is needed.

See here for more details.

Params:
  • dateString – the String to be interpreted as a Date
  • dateBackwardCompatibility – true to correct Date values that may have been written using versions of this library prior to 2.0.0; otherwise, false
Returns:the corresponding Date object
/** * Given a String representing a date in a form of the ISO8601 pattern, generates a Date representing it * with up to millisecond precision. Use {@link #parseDate(String)} instead unless * <code>dateBackwardCompatibility</code> is needed. * <p> * See <a href="http://go.microsoft.com/fwlink/?LinkId=523753">here</a> for more details. * * @param dateString * the <code>String</code> to be interpreted as a <code>Date</code> * @param dateBackwardCompatibility * <code>true</code> to correct Date values that may have been written * using versions of this library prior to 2.0.0; otherwise, <code>false</code> * * @return the corresponding <code>Date</code> object */
public static Date parseDate(String dateString, boolean dateBackwardCompatibility) { if (!dateBackwardCompatibility) { return parseDate(dateString); } final int beginMilliIndex = 20; // Length of "yyyy-MM-ddTHH:mm:ss." final int endTenthMilliIndex = 24; // Length of "yyyy-MM-ddTHH:mm:ss.SSSS" // Check whether the millisecond and tenth of a millisecond digits are all 0. if (dateString.length() > endTenthMilliIndex && "0000".equals(dateString.substring(beginMilliIndex, endTenthMilliIndex))) { // Remove the millisecond and tenth of a millisecond digits. // Treat the final three digits (ticks) as milliseconds. dateString = dateString.substring(0, beginMilliIndex) + dateString.substring(endTenthMilliIndex); } return parseDate(dateString); }
Determines which location can the listing command target by looking at the continuation token.
Params:
  • token – Continuation token
Returns: Location mode
/** * Determines which location can the listing command target by looking at the * continuation token. * * @param token * Continuation token * @return * Location mode */
public static RequestLocationMode getListingLocationMode(ResultContinuation token) { if ((token != null) && token.getTargetLocation() != null) { switch (token.getTargetLocation()) { case PRIMARY: return RequestLocationMode.PRIMARY_ONLY; case SECONDARY: return RequestLocationMode.SECONDARY_ONLY; default: throw new IllegalArgumentException(String.format(SR.ARGUMENT_OUT_OF_RANGE_ERROR, "token", token.getTargetLocation())); } } return RequestLocationMode.PRIMARY_OR_SECONDARY; }
Equivalent of String.join() in Java 8.
Params:
  • delimiter – Characters to join strings with.
  • strings – Strings to join.
Returns: The joined string.
/** * Equivalent of String.join() in Java 8. * * @param delimiter Characters to join strings with. * @param strings Strings to join. * @return The joined string. */
public static String stringJoin(CharSequence delimiter, final String... strings) { return stringJoin(delimiter, new Iterable<String>() { @Override public Iterator<String> iterator() { return new Iterator<String>() { private final String[] internalArray = strings; private int index; @Override public boolean hasNext() { return internalArray.length > index; } @Override public String next() { return internalArray[index++]; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }); }
Equivalent of String.join() in Java 8.
Params:
  • delimiter – Characters to join strings with.
  • strings – Strings to join.
Returns: The joined string.
/** * Equivalent of String.join() in Java 8. * * @param delimiter Characters to join strings with. * @param strings Strings to join. * @return The joined string. */
public static String stringJoin(CharSequence delimiter, Iterable<String> strings) { StringBuilder sb = new StringBuilder(); Iterator<String> iterator = strings.iterator(); if (iterator.hasNext()) { sb.append(iterator.next()); } while (iterator.hasNext()) { sb.append(delimiter); sb.append(iterator.next()); } return sb.toString(); } public static List<byte[]> splitOnPattern(byte[] array, byte[] pattern) { List<Integer> sortedPatternIndices = findAllPatternOccurences(array, pattern); List<byte[]> tokens = new ArrayList<>(sortedPatternIndices.size() + 1); int nextTokenStart = 0; for (int patternStart : sortedPatternIndices) { // filter out empty ranges if (patternStart - 1 > nextTokenStart) { tokens.add(Arrays.copyOfRange(array, nextTokenStart, patternStart)); } nextTokenStart = patternStart + pattern.length; } if (nextTokenStart < array.length - 1) { tokens.add(Arrays.copyOfRange(array, nextTokenStart, array.length)); } return tokens; }
Scans a byte array for all occurrences of a pattern, with no overlap of occurrence spans.
Params:
  • array – The array to search.
  • pattern – The pattern to search for.
Returns: A list of starting indices of all occurrences of the pattern, in ascending order.
/** * Scans a byte array for all occurrences of a pattern, with no overlap of occurrence spans. * * @param array * The array to search. * @param pattern * The pattern to search for. * * @return * A list of starting indices of all occurrences of the pattern, in ascending order. */
public static List<Integer> findAllPatternOccurences(byte[] array, byte[] pattern) { List<Integer> indices = new ArrayList<>(); int offset = 0; while (true) { int result = findPattern(array, pattern, offset); // if pattern not found, we reached the end. if (result == -1) { break; } indices.add(result); offset = result + pattern.length; // if no room left for pattern, don't bother checking. if (offset > array.length - pattern.length + 1) { break; } } // guaranteed to be in ascending order return indices; }
Scans a byte array for the first occurrence of a pattern, starting at the offset provided.
Params:
  • array – The array to search.
  • pattern – The pattern to search for.
  • scanOffset – Where to begin the search.
Returns: The starting index of the found pattern. -1 if not found.
/** * Scans a byte array for the first occurrence of a pattern, starting at the offset provided. * * @param array * The array to search. * @param pattern * The pattern to search for. * @param scanOffset * Where to begin the search. * * @return * The starting index of the found pattern. -1 if not found. */
public static int findPattern(byte[] array, byte[] pattern, int scanOffset) { for (int i = scanOffset; i < array.length - pattern.length + 1; i++) { if (isMatch(array, pattern, i)) { return i; } } return -1; }
Checks if a byte pattern exists in the array at the given position.
Params:
  • array – The array to check for the pattern.
  • pattern – The pattern to check for.
  • arrayPos – Where to check for the pattern.
Throws:
Returns: Whether the pattern exists at this point of the array.
/** * Checks if a byte pattern exists in the array at the given position. * * @param array * The array to check for the pattern. * @param pattern * The pattern to check for. * @param arrayPos * Where to check for the pattern. * * @return * Whether the pattern exists at this point of the array. * * @throws IndexOutOfBoundsException * Throws if trying to check for the pattern outside the bounds of the array. */
private static boolean isMatch(byte[] array, byte[] pattern, int arrayPos) { for (int i = 0; i < pattern.length; i++) { if (pattern[i] != array[arrayPos + i]) { return false; } } return true; } }