- * Setting this access condition modifies the request to include the HTTP If-Match conditional header. If - * this access condition is set, the operation is performed only if the ETag of the resource matches the specified - * ETag. + * Setting this access condition modifies the request to include the HTTP If-Match conditional header. If this + * access condition is set, the operation is performed only if the ETag of the resource matches the specified ETag. *
* For more information, see Specifying * Conditional Headers for Blob Service Operations. @@ -84,8 +83,8 @@ public static AccessCondition generateIfModifiedSinceCondition(final Date lastMo * Returns an access condition such that an operation will be performed only if the resource's ETag value does not * match the specified ETag value. *
- * Setting this access condition modifies the request to include the HTTP If-None-Match conditional header. - * If this access condition is set, the operation is performed only if the ETag of the resource does not match the + * Setting this access condition modifies the request to include the HTTP If-None-Match conditional header. If + * this access condition is set, the operation is performed only if the ETag of the resource does not match the * specified ETag. *
* For more information, see Specifying
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/CloudStorageAccount.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/CloudStorageAccount.java
index 8818215a944ee..8beb56163e4cc 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/CloudStorageAccount.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/CloudStorageAccount.java
@@ -24,6 +24,7 @@
import com.microsoft.windowsazure.services.blob.client.CloudBlobClient;
import com.microsoft.windowsazure.services.core.storage.utils.Utility;
import com.microsoft.windowsazure.services.queue.client.CloudQueueClient;
+import com.microsoft.windowsazure.services.table.client.CloudTableClient;
/**
* Represents a Windows Azure storage account.
@@ -538,6 +539,26 @@ public CloudQueueClient createCloudQueueClient() {
return new CloudQueueClient(this.getQueueEndpoint(), this.getCredentials());
}
+ /**
+ * Creates a new table service client.
+ *
+ * @return A client object that uses the Table service endpoint.
+ */
+ public CloudTableClient createCloudTableClient() {
+ if (this.getTableEndpoint() == null) {
+ throw new IllegalArgumentException("No table endpoint configured.");
+ }
+
+ if (this.credentials == null) {
+ throw new IllegalArgumentException("No credentials provided.");
+ }
+
+ if (!this.credentials.canCredentialsSignRequest()) {
+ throw new IllegalArgumentException("CloudTableClient requires a credential that can sign request");
+ }
+ return new CloudTableClient(this.getTableEndpoint(), this.getCredentials());
+ }
+
/**
* Returns the endpoint for the Blob service, as configured for the storage account. This method is not supported
* when using shared access signature credentials.
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java
index 492320796e708..7676efe058daa 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java
@@ -287,7 +287,7 @@ public static class HeaderConstants {
/**
* Specifies the value to use for UserAgent header.
*/
- public static final String USER_AGENT_VERSION = "Client v0.1.1";
+ public static final String USER_AGENT_VERSION = "Client v0.1.2";
}
/**
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/ResultSegment.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/ResultSegment.java
index debb82c4c6a89..39d2c8b737068 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/ResultSegment.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/ResultSegment.java
@@ -39,9 +39,9 @@ public class ResultSegment
- * The server timeout interval begins at the time that the complete request has been received by the service, and
- * the server begins processing the response. If the timeout interval elapses before the response is returned to the
+ * The server timeout interval begins at the time that the complete request has been received by the service, and the
+ * server begins processing the response. If the timeout interval elapses before the response is returned to the
* client, the operation times out. The timeout interval resets with each retry, if the request is retried.
*
- * The default timeout interval for a request made via the service client is 90 seconds. You can change this value
- * on the service client by setting this property, so that all subsequent requests made via the service client will
- * use the new timeout interval. You can also change this value for an individual request, by setting the
+ * The default timeout interval for a request made via the service client is 90 seconds. You can change this value on
+ * the service client by setting this property, so that all subsequent requests made via the service client will use
+ * the new timeout interval. You can also change this value for an individual request, by setting the
* {@link RequestOptions#timeoutIntervalInMs} property.
*
* If you are downloading a large blob, you should increase the value of the timeout beyond the default value.
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentials.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentials.java
index 84d4c055976ac..84b4c194ac8fb 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentials.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentials.java
@@ -39,8 +39,8 @@ public abstract class StorageCredentials {
* Either include an account name with an account key (specifying values for
* {@link CloudStorageAccount#ACCOUNT_NAME_NAME} and {@link CloudStorageAccount#ACCOUNT_KEY_NAME} ), or a
* shared access signature (specifying a value for
- * {@link CloudStorageAccount#SHARED_ACCESS_SIGNATURE_NAME} ). If you use an account name and account
- * key, do not include a shared access signature, and vice versa.
+ * {@link CloudStorageAccount#SHARED_ACCESS_SIGNATURE_NAME} ). If you use an account name and account key,
+ * do not include a shared access signature, and vice versa.
*
* @return A {@link StorageCredentials} object representing the storage credentials determined from the name/value
* pairs.
@@ -81,8 +81,8 @@ protected static StorageCredentials tryParseCredentials(final HashMap
- * The format for the connection string is in the pattern "keyname=value". Multiple key/value
- * pairs can be separated by a semi-colon, for example, "keyname1=value1;keyname2=value2".
+ * The format for the connection string is in the pattern "keyname=value". Multiple key/value pairs
+ * can be separated by a semi-colon, for example, "keyname1=value1;keyname2=value2".
*
* @return A {@link StorageCredentials} object representing the storage credentials determined from the connection
* string.
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentialsSharedAccessSignature.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentialsSharedAccessSignature.java
index c33ce78d64590..2f3e44a380ed1 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentialsSharedAccessSignature.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageCredentialsSharedAccessSignature.java
@@ -96,8 +96,8 @@ public String computeHmac256(final String value) {
/**
* Computes a signature for the specified string using the HMAC-SHA256 algorithm with the specified operation
- * context. This is not a valid operation for objects of type
+ * For more information about OData, see the Open Data Protocol website. For more
+ * information about the AtomPub format used in OData, see OData Protocol Atom Format.
+ */
+class AtomPubParser {
+ /**
+ * Reserved for internal use. A static factory method to construct an
+ * The {@link CloudTableClient} class encapsulates the base URI for the Table service endpoint and the credentials for
+ * accessing the storage account, and provides methods to create, delete, list, and query tables, as well as methods to
+ * execute operations and queries on table entities. These methods invoke Storage Service REST API operations to make
+ * the requests and obtain the results that are returned.
+ *
+ * A Table service endpoint is the base URI for Table service resources, including the DNS name of the storage account:
+ *
+ * The credentials can be a combination of the storage account name and a key, or a shared access signature. For more
+ * information, see the MSDN topic Authenticating Access to Your Storage
+ * Account.
+ *
+ */
+public final class CloudTableClient extends ServiceClient {
+
+ /**
+ * Reserved for internal use. An {@link EntityResolver} that projects table entity data as a
+ * A {@link CloudTableClient} initialized with this constructor must have storage account credentials added before
+ * it can be used to access the Windows Azure storage service.
+ *
+ * @param baseUri
+ * A
+ * This method invokes the Create
+ * Table REST API to create the specified table, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method invokes the Create
+ * Table REST API to create the specified table, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method first invokes the Query
+ * Tables REST API to determine if the table exists, and if not, invokes the Create Table Storage Service REST
+ * API to create the specified table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * @param tableName
+ * A
+ * This method first invokes the Query
+ * Tables REST API to determine if the table exists, and if not, invokes the Create Table Storage Service REST
+ * API to create the specified table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method invokes the Delete
+ * Table REST API to delete the specified table and any data it contains, using the Table service endpoint and
+ * storage account credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method invokes the Delete
+ * Table REST API to delete the specified table and any data it contains, using the Table service endpoint and
+ * storage account credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method first invokes the Query
+ * Tables REST API to determine if the table exists, and if so, invokes the Delete Table Storage Service REST
+ * API to delete the table and any data it contains, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method first invokes the Query
+ * Tables REST API to determine if the table exists, and if so, invokes the Delete Table Storage Service REST
+ * API to delete the table and any data it contains, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method invokes the Query
+ * Tables REST API to determine if the table exists, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method invokes the Query
+ * Tables REST API to determine if the table exists, using the Table service endpoint and storage account
+ * credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method invokes an Entity Group
+ * Transaction on the REST API to execute the specified batch operation on the table as an atomic unit, using
+ * the Table service endpoint and storage account credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method invokes an Entity Group
+ * Transaction on the REST API to execute the specified batch operation on the table as an atomic unit, using
+ * the Table service endpoint and storage account credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method will invoke the Table
+ * Service REST API to execute the specified operation on the table, using the Table service endpoint and
+ * storage account credentials of this instance.
+ *
+ * @param tableName
+ * A
+ * This method will invoke the Table
+ * Service REST API to execute the specified operation on the table, using the Table service endpoint and
+ * storage account credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param tableName
+ * A
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use.
+ * @param resolver
+ * An {@link EntityResolver} instance which creates a projection of the table query result entities into
+ * the specified type
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use.
+ * @param resolver
+ * An {@link EntityResolver} instance which creates a projection of the table query result entities into
+ * the specified type
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use,
+ * specialized for a type T implementing {@link TableEntity}.
+ *
+ * @return
+ * A collection implementing the
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use,
+ * specialized for a type T implementing {@link TableEntity}.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use.
+ * @param resolver
+ * An {@link EntityResolver} instance which creates a projection of the table query result entities into
+ * the specified type
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use.
+ * @param resolver
+ * An {@link EntityResolver} instance which creates a projection of the table query result entities into
+ * the specified type
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use,
+ * specialized for a type T implementing {@link TableEntity}.
+ * @param continuationToken
+ * A {@link ResultContinuation} object representing a continuation token from the server when the
+ * operation returns a partial result. Specify
+ * This method will invoke a Query
+ * Entities operation on the Table
+ * Service REST API to query the table, using the Table service endpoint and storage account credentials of this
+ * instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param query
+ * A {@link TableQuery} instance specifying the table to query and the query parameters to use,
+ * specialized for a type T implementing {@link TableEntity}.
+ * @param continuationToken
+ * A {@link ResultContinuation} object representing a continuation token from the server when the
+ * operation returns a partial result. Specify
+ * This method invokes the Query
+ * Tables REST API to list the table names, using the Table service endpoint and storage account credentials of
+ * this instance.
+ *
+ * @return
+ * An
+ * This method invokes the Query
+ * Tables REST API to list the table names that match the prefix, using the Table service endpoint and storage
+ * account credentials of this instance.
+ *
+ * @param prefix
+ * A
+ * This method invokes the Query
+ * Tables REST API to list the table names that match the prefix, using the Table service endpoint and storage
+ * account credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param prefix
+ * A
+ * This method invokes the Query
+ * Tables REST API to list the table names, using the Table service endpoint and storage account credentials of
+ * this instance.
+ *
+ * @param prefix
+ * A
+ * This method invokes the Query
+ * Tables REST API to list the table names that match the prefix, using the Table service endpoint and storage
+ * account credentials of this instance.
+ *
+ * @param prefix
+ * A
+ * This method invokes the Query
+ * Tables REST API to list the table names that match the prefix, using the Table service endpoint and storage
+ * account credentials of this instance.
+ *
+ * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the
+ * operation.
+ *
+ * @param prefix
+ * A
+ * For more information about OData, see the Open Data Protocol website.
+ *
+ * For an overview of the available EDM primitive data types and names, see the Primitive Data Types section of the
+ * OData Protocol Overview.
+ *
+ * The Abstract Type System used to define the primitive types supported by OData is defined in detail in [MC-CSDL] (section 2.2.1).
+ */
+public enum EdmType {
+ /**
+ * Null Represents the absence of a value
+ */
+ NULL,
+
+ /**
+ * Edm.Binary Represents fixed- or variable-length binary data
+ */
+ BINARY,
+
+ /**
+ * Edm.Boolean Represents the mathematical concept of binary-valued logic
+ */
+ BOOLEAN,
+
+ /**
+ * Edm.Byte Represents a unsigned 8-bit integer value
+ */
+ BYTE,
+
+ /**
+ * Edm.DateTime Represents date and time with values ranging from 12:00:00 midnight, January 1,
+ * 1753 A.D. through 11:59:59 P.M, December 9999 A.D.
+ */
+ DATE_TIME,
+
+ /**
+ * Edm.Decimal Represents numeric values with fixed precision and scale. This type can describe a
+ * numeric value ranging from negative 10^255 + 1 to positive 10^255 -1
+ */
+ DECIMAL,
+
+ /**
+ * Edm.Double Represents a floating point number with 15 digits precision that can represent values
+ * with approximate range of +/- 2.23e -308 through +/- 1.79e +308
+ */
+ DOUBLE,
+
+ /**
+ * Edm.Single Represents a floating point number with 7 digits precision that can represent values
+ * with approximate range of +/- 1.18e -38 through +/- 3.40e +38
+ */
+ SINGLE,
+
+ /**
+ * Edm.Guid Represents a 16-byte (128-bit) unique identifier value
+ */
+ GUID,
+
+ /**
+ * Edm.Int16 Represents a signed 16-bit integer value
+ */
+ INT16,
+
+ /**
+ * Edm.Int32 Represents a signed 32-bit integer value
+ */
+ INT32,
+
+ /**
+ * Edm.Int64 Represents a signed 64-bit integer value
+ */
+ INT64,
+
+ /**
+ * Edm.SByte Represents a signed 8-bit integer value
+ */
+ SBYTE,
+
+ /**
+ * Edm.String Represents fixed- or variable-length character data
+ */
+ STRING,
+
+ /**
+ * Edm.Time Represents the time of day with values ranging from 0:00:00.x to 23:59:59.y, where x
+ * and y depend upon the precision
+ */
+ TIME,
+
+ /**
+ * Edm.DateTimeOffset Represents date and time as an Offset in minutes from GMT, with values
+ * ranging from 12:00:00 midnight, January 1, 1753 A.D. through 11:59:59 P.M, December 9999 A.D
+ */
+ DATE_TIME_OFFSET;
+
+ /**
+ * Parses an EDM data type name and return the matching {@link EdmType} enumeration value. A
+ * {@link EntityProperty} provides overloaded constructors and overloads of the
+ * Use one of the
+ * This interface is useful for converting directly from table entity data to a client object type without requiring a
+ * separate table entity class type that deserializes every property individually. For example, a client can perform a
+ * client side projection of a Customer entity by simply returning the
+ * This method will invoke the Storage Service REST API to execute this table operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint and storage account
+ * credentials to use.
+ * @param tableName
+ * A
+ * Example:
+ *
+ *
+ *
+ * This example shows how the methods that would get and set an entity property named ObjectPropertyName in the
+ * default case can be annotated to get and set an entity property named EntityPropertyName. See the
+ * documentation for {@link TableServiceEntity} for more information on using reflection-based serialization and
+ * deserialization.
+ *
+ * @see Ignore
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface StoreAs {
+ public String name();
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableBatchOperation.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableBatchOperation.java
new file mode 100644
index 0000000000000..596e519612c30
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableBatchOperation.java
@@ -0,0 +1,514 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import javax.xml.stream.XMLStreamReader;
+
+import com.microsoft.windowsazure.services.core.storage.Constants;
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;
+
+/**
+ * A class which represents a batch operation. A batch operation is a collection of table operations which are executed
+ * by the Storage Service REST API as a single atomic operation, by invoking an Entity Group Transaction.
+ *
+ * A batch operation may contain up to 100 individual table operations, with the requirement that each operation entity
+ * must have same partition key. A batch with a retrieve operation cannot contain any other operations. Note that the
+ * total payload of a batch operation is limited to 4MB.
+ */
+public class TableBatchOperation extends ArrayList
+ * This method will invoke the Storage Service REST API to execute this batch operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint and storage account
+ * credentials to use.
+ * @param tableName
+ * A
+ * The Storage client library includes two implementations of {@link TableEntity} that provide for simple property
+ * access and serialization:
+ *
+ * {@link DynamicTableEntity} implements {@link TableEntity} and provides a simple property map to store and retrieve
+ * properties. Use a {@link DynamicTableEntity} for simple access to entity properties when only a subset of properties
+ * are returned (for example, by a select clause in a query), or for when your query can return multiple entity types
+ * with different properties. You can also use this type to perform bulk table updates of heterogeneous entities without
+ * losing property information.
+ *
+ * {@link TableServiceEntity} is an implementation of {@link TableEntity} that uses reflection-based serialization and
+ * deserialization behavior in its
+ * Any class that implements {@link TableEntity} can take advantage of the automatic reflection-based serialization and
+ * deserialization behavior in {@link TableServiceEntity} by invoking the static methods
+ *
+ *
+ * and
+ *
+ *
+ * where PropertyName is a property name for the table entity, and type is a Java type compatible with
+ * the EDM data type of the property. See the table in the class description for {@link TableServiceEntity} for a map of
+ * property types to their Java equivalents. The {@link StoreAs} annotation may be applied with a
+ *
+ * @see TableServiceEntity
+ * @see DynamicTableEntity
+ */
+public interface TableEntity {
+
+ /**
+ * Gets the Etag value for the entity. This value is used to determine if the table entity has changed since it was
+ * last read from Windows Azure storage.
+ *
+ * @return
+ * A
+ * Use the static factory methods to construct {@link TableOperation} instances for operations on tables that insert,
+ * update, merge, delete, replace or retrieve table entities. To execute a {@link TableOperation} instance, call the
+ *
+ * This method will invoke the Delete
+ * Entity REST API to execute this table operation, using the Table service endpoint and storage account
+ * credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint, storage account
+ * credentials, and any additional query parameters.
+ * @param tableName
+ * A
+ * This method will invoke the Insert Entity REST API to execute this table operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint, storage account
+ * credentials, and any additional query parameters.
+ * @param tableName
+ * A
+ * This method will invoke the Merge Entity REST API to execute this table operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint, storage account
+ * credentials, and any additional query parameters.
+ * @param tableName
+ * A
+ * This method will invoke the Storage Service REST API to execute this table operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint, storage account
+ * credentials, and any additional query parameters.
+ * @param tableName
+ * A
+ * This method will invoke the Storage Service REST API to execute this table operation, using the Table service
+ * endpoint and storage account credentials in the {@link CloudTableClient} object.
+ *
+ * @param client
+ * A {@link CloudTableClient} instance specifying the Table service endpoint, storage account
+ * credentials, and any additional query parameters.
+ * @param tableName
+ * A
+ * To create a table query with fluent syntax, the {@link #from} static factory method and the {@link #where},
+ * {@link #select}, and {@link #take} mutator methods each return a reference to the object which can be chained into a
+ * single expression. Use the {@link TableQuery#from(String, Class)} static class factory method to create a
+ *
+ * As an example, you could construct a table query using fluent syntax:
+ *
+ *
+ * This example creates a query on the "Products" table for all entities where the PartitionKey value is "ProductsMNO"
+ * and the RowKey value is greater than or equal to "Napkin" and requests the first 25 matching entities, selecting only
+ * the common properties and the property named "InventoryCount", and returns them as {@link DynamicTableEntity}
+ * objects.
+ *
+ * Filter expressions for use with the {@link #where} method or {@link #setFilterString} method can be created using
+ * fluent syntax with the overloaded {@link #generateFilterCondition} methods and {@link #combineFilters} method, using
+ * the comparison operators defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}.
+ * Note that the first operand in a filter comparison must be a property name, and the second operand must evaluate to a
+ * constant. The PartitionKey and RowKey property values are
+ * The values that may be used in table queries are explained in more detail in the MSDN topic Querying Tables and Entities, but note
+ * that the space characters within values do not need to be URL-encoded, as this will be done when the query is
+ * executed.
+ *
+ * The {@link TableQuery#TableQuery(String, Class)} constructor and {@link TableQuery#from(String, Class)} static
+ * factory methods require a class type which implements {@link TableEntity} and contains a nullary constructor. If the
+ * query will be executed using an {@link EntityResolver}, the caller may specify {@link TableServiceEntity}
+ *
+ * The created {@link TableQuery} instance is specialized for table entities of the specified class type T, using
+ * the table with the specified name as data source. Callers may specify {@link TableServiceEntity}
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ *
+ * This statement sets
+ *
+ * Note that the system properties
+ * If the value returned by
+ * Note that the system properties
+ * Callers may specify {@link TableServiceEntity}
+ * Note that the system properties
+ * Filter expressions for use with the {@link #setFilterString} method can be created using fluent syntax with the
+ * overloaded {@link #generateFilterCondition} methods and {@link #combineFilters} method, using the comparison
+ * operators defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}. Note that
+ * the first operand in a filter comparison must be a property name, and the second operand must evaluate to a
+ * constant. The PartitionKey and RowKey property values are
+ *
+ * The values that may be used in table queries are explained in more detail in the MSDN topic
+ *
+ * Querying Tables and Entities,
+ * but note that the space characters within values do not need to be URL-encoded, as this will be done when the
+ * query is executed.
+ *
+ * Note that no more than 15 discrete comparisons are permitted within a filter string.
+ *
+ * @param filterString
+ * A
+ * If the value specified for the
+ * If the value specified for the
+ * Filter expressions for use with the {@link #where} method can be created using fluent syntax with the overloaded
+ * {@link #generateFilterCondition} methods and {@link #combineFilters} method, using the comparison operators
+ * defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}. Note that the first
+ * operand in a filter comparison must be a property name, and the second operand must evaluate to a constant. The
+ * PartitionKey and RowKey property values are
+ *
+ * The values that may be used in table queries are explained in more detail in the MSDN topic
+ *
+ * Querying Tables and Entities,
+ * but note that the space characters within values do not need to be URL-encoded, as this will be done when the
+ * query is executed.
+ *
+ * Note that no more than 15 discrete comparisons are permitted within a filter string.
+ *
+ * @param filter
+ * A
+ * The use of reflection allows subclasses of {@link TableServiceEntity} to be serialized and deserialized without
+ * having to implement the serialization code themselves. When both a getter method and setter method are found for a
+ * given property name and data type, then the appropriate method is invoked automatically to serialize or deserialize
+ * the data. To take advantage of the automatic serialization code, your table entity classes should provide getter and
+ * setter methods for each property in the corresponding table entity in Windows Azure table storage. The reflection
+ * code looks for getter and setter methods in pairs of the form
+ *
+ *
+ * and
+ *
+ *
+ * where PropertyName is a property name for the table entity, and type is a Java type compatible with
+ * the EDM data type of the property. See the table below for a map of property types to their Java equivalents. The
+ * {@link StoreAs} annotation may be applied with a
+ * The following table shows the supported property data types in Windows Azure storage and the corresponding Java types
+ * when deserialized.
+ *
+ * See the MSDN topic Understanding the
+ * Table Service Data Model for an overview of tables, entities, and properties as used in the Windows Azure Storage
+ * service.
+ *
+ * For an overview of the available EDM primitive data types and names, see the
+ *
+ * Primitive Data Types section of
+ * the OData Protocol Overview.
+ *
+ *
+ * @see EdmType
+ */
+public class TableServiceEntity implements TableEntity {
+ /**
+ * Deserializes the table entity property map into the specified object instance using reflection.
+ *
+ * This static method takes an object instance that represents a table entity type and uses reflection on its class
+ * type to find methods to deserialize the data from the property map into the instance.
+ *
+ * Each property name and data type in the properties map is compared with the methods in the class type for a pair
+ * of getter and setter methods to use for serialization and deserialization. The class is scanned for methods with
+ * names that match the property name with "get" and "set" prepended, or with the {@link StoreAs} annotation set
+ * with the property name. The methods must have return types or parameter data types that match the data type of
+ * the corresponding {@link EntityProperty} value. If such a pair is found, the data is copied into the instance
+ * object by invoking the setter method on the instance. Properties that do not match a method pair by name and data
+ * type are not copied.
+ *
+ * @param instance
+ * A reference to an instance of a class implementing {@link TableEntity} to deserialize the table entity
+ * data into.
+ * @param properties
+ * A map of
+ * This static method takes an object instance that represents a table entity type and uses reflection on its class
+ * type to find methods to serialize the data from the instance into the property map.
+ *
+ * Each property name and data type in the properties map is compared with the methods in the class type for a pair
+ * of getter and setter methods to use for serialization and deserialization. The class is scanned for methods with
+ * names that match the property name with "get" and "set" prepended, or with the {@link StoreAs} annotation set
+ * with the property name. The methods must have return types or parameter data types that match the data type of
+ * the corresponding {@link EntityProperty} value. If such a pair is found, the data is copied from the instance
+ * object by invoking the getter method on the instance. Properties that do not have a method pair with matching
+ * name and data type are not copied.
+ *
+ * @param instance
+ * A reference to an instance of a class implementing {@link TableEntity} to serialize the table entity
+ * data from.
+ * @return
+ * A map of
+ * This method invokes {@link TableServiceEntity#readEntityWithReflection} to populate the table entity instance the
+ * method is called on using reflection. Table entity classes that extend {@link TableServiceEntity} can take
+ * advantage of this behavior by implementing getter and setter methods for the particular properties of the table
+ * entity in Windows Azure storage the class represents.
+ *
+ * Override this method in classes that extend {@link TableServiceEntity} to invoke custom serialization code.
+ *
+ * @param properties
+ * The
+ * This method invokes {@link #writeEntityWithReflection} to serialize the table entity instance the method is
+ * called on using reflection. Table entity classes that extend {@link TableServiceEntity} can take advantage of
+ * this behavior by implementing getter and setter methods for the particular properties of the table entity in
+ * Windows Azure storage the class represents. Note that the property names "PartitionKey", "RowKey", and
+ * "Timestamp" are reserved and will be ignored if set on other methods with the {@link StoreAs} annotation.
+ *
+ * Override this method in classes that extend {@link TableServiceEntity} to invoke custom serialization code.
+ *
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @return
+ * A ResultSegment
class.
@@ -115,11 +115,11 @@ public int getRemainingPageResults() {
}
/**
- * Returns an enumerable set of results from the blob service.
+ * Returns an enumerable set of results from the service.
*
- * @return The results retrieved from the blob service.
+ * @return The results retrieved from the service.
*/
- public IterableStorageCredentialsSharedAccessSignature
- * so the method merely returns null
.
+ * context. This is not a valid operation for objects of type StorageCredentialsSharedAccessSignature
so
+ * the method merely returns null
.
*
* @param value
* The UTF-8-encoded string to sign.
@@ -130,8 +130,8 @@ public String computeHmac512(final String value) {
/**
* Computes a signature for the specified string using the HMAC-SHA512 algorithm with the specified operation
- * context. This is not a valid operation for objects of type StorageCredentialsSharedAccessSignature
- * so the method merely returns null
.
+ * context. This is not a valid operation for objects of type StorageCredentialsSharedAccessSignature
so
+ * the method merely returns null
.
*
* @param value
* The UTF-8-encoded string to sign.
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageErrorCodeStrings.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageErrorCodeStrings.java
index 5e37420554264..624a31467311d 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageErrorCodeStrings.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/StorageErrorCodeStrings.java
@@ -188,6 +188,11 @@ public final class StorageErrorCodeStrings {
*/
public static final String SERVER_BUSY = "ServerBusy";
+ /**
+ * Table Already Exists
+ */
+ public static final String TABLE_ALREADY_EXISTS = "TableAlreadyExists";
+
/**
* One or more header values are not supported.
*/
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/implementation/ExecutionEngine.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/implementation/ExecutionEngine.java
index 02e3554479058..e30d5f1e45440 100644
--- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/implementation/ExecutionEngine.java
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/implementation/ExecutionEngine.java
@@ -12,6 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.microsoft.windowsazure.services.core.storage.utils.implementation;
import java.io.IOException;
@@ -34,6 +35,7 @@
import com.microsoft.windowsazure.services.core.storage.RetryResult;
import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.table.client.TableServiceException;
/**
* RESERVED FOR INTERNAL USE. A class that handles execution of StorageOperations and enforces retry policies.
@@ -166,6 +168,17 @@ public static XMLStreamWriter
instance based on
+ * the specified OutputStream
.
+ *
+ * @param outStream
+ * The OutputStream
instance to create an XMLStreamWriter
on.
+ * @return
+ * An XMLStreamWriter
instance based on the specified OutputStream
.
+ * @throws XMLStreamException
+ * if an error occurs while creating the stream.
+ */
+ protected static XMLStreamWriter generateTableWriter(final OutputStream outStream) throws XMLStreamException {
+ final XMLOutputFactory xmlOutFactoryInst = XMLOutputFactory.newInstance();
+ return xmlOutFactoryInst.createXMLStreamWriter(outStream, "UTF-8");
+ }
+
+ /**
+ * Reserved for internal use. Parses the operation response as an entity. Parses the result returned in the
+ * specified stream in AtomPub format into a {@link TableResult} containing an entity of the specified class type
+ * projected using the specified resolver.
+ *
+ * @param xmlr
+ * An XMLStreamReader
on the input stream.
+ * @param clazzType
+ * The class type T
implementing {@link TableEntity} for the entity returned. Set to
+ * null
to ignore the returned entity and copy only response properties into the
+ * {@link TableResult} object.
+ * @param resolver
+ * An {@link EntityResolver} instance to project the entity into an instance of type R
. Set
+ * to null
to return the entity as an instance of the class type T
.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @return
+ * A {@link TableResult} containing the parsed entity result of the operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs while accessing the stream.
+ * @throws ParseException
+ * if an error occurs while parsing the stream.
+ * @throws InstantiationException
+ * if an error occurs while constructing the result.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection while parsing the result.
+ * @throws StorageException
+ * if a storage service error occurs.
+ */
+ protected static InputStream
to read the data to parse from.
+ * @param clazzType
+ * The class type T
implementing {@link TableEntity} for the entities returned. Set to
+ * null
to ignore the returned entities and copy only response properties into the
+ * {@link TableResult} objects.
+ * @param resolver
+ * An {@link EntityResolver} instance to project the entities into instances of type R
. Set
+ * to null
to return the entities as instances of the class type T
.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @return
+ * An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation
+ * response.
+ *
+ * @throws XMLStreamException
+ * if an error occurs while accessing the stream.
+ * @throws ParseException
+ * if an error occurs while parsing the stream.
+ * @throws InstantiationException
+ * if an error occurs while constructing the result.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection while parsing the result.
+ * @throws StorageException
+ * if a storage service error occurs.
+ */
+ @SuppressWarnings("unchecked")
+ protected static XMLStreamReader
using the specified class type and optionally projects the entity result with the
+ * specified resolver into a {@link TableResult} object.
+ *
+ * @param xmlr
+ * The XMLStreamReader
to read the data to parse from.
+ * @param httpStatusCode
+ * The HTTP status code returned with the operation response.
+ * @param clazzType
+ * The class type T
implementing {@link TableEntity} for the entity returned. Set to
+ * null
to ignore the returned entity and copy only response properties into the
+ * {@link TableResult} object.
+ * @param resolver
+ * An {@link EntityResolver} instance to project the entity into an instance of type R
. Set
+ * to null
to return the entitys as instance of the class type T
.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @return
+ * A {@link TableResult} object with the parsed operation response.
+ *
+ * @throws XMLStreamException
+ * if an error occurs while accessing the stream.
+ * @throws ParseException
+ * if an error occurs while parsing the stream.
+ * @throws InstantiationException
+ * if an error occurs while constructing the result.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection while parsing the result.
+ * @throws StorageException
+ * if a storage service error occurs.
+ */
+ protected static String
property names to {@link EntityProperty} data typed values.
+ *
+ * @param xmlr
+ * The XMLStreamReader
to read the data from.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ *
+ * @return
+ * A java.util.HashMap
containing a map of String
property names to
+ * {@link EntityProperty} data typed values found in the entity data.
+ * @throws XMLStreamException
+ * if an error occurs accessing the stream.
+ * @throws ParseException
+ * if an error occurs converting the input to a particular data type.
+ */
+ protected static HashMaptrue
and a reference to an entity within a table when
false
.
+ * @param xmlw
+ * The XMLStreamWriter
to write the entity to.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs accessing the stream.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ */
+ protected static void writeEntityToStream(final TableEntity entity, final boolean isTableEntry,
+ final XMLStreamWriter xmlw, final OperationContext opContext) throws XMLStreamException, StorageException {
+ final HashMapOutputStream
as a complete XML
+ * document.
+ *
+ * @param entity
+ * The instance implementing {@link TableEntity} to write to the output stream.
+ * @param isTableEntry
+ * A flag indicating the entity is a reference to a table at the top level of the storage service when
+ * true
and a reference to an entity within a table when
false
.
+ * @param outStream
+ * The OutputStream
to write the entity to.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs creating or accessing the stream.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ */
+ protected static void writeSingleEntityToStream(final TableEntity entity, final boolean isTableEntry,
+ final OutputStream outStream, final OperationContext opContext) throws XMLStreamException, StorageException {
+ final XMLStreamWriter xmlw = AtomPubParser.generateTableWriter(outStream);
+ writeSingleEntityToStream(entity, isTableEntry, xmlw, opContext);
+ }
+
+ /**
+ * Reserved for internal use. Writes a single entity to the specified XMLStreamWriter
as a complete XML
+ * document.
+ *
+ * @param entity
+ * The instance implementing {@link TableEntity} to write to the output stream.
+ * @param isTableEntry
+ * A flag indicating the entity is a reference to a table at the top level of the storage service when
+ * true
and a reference to an entity within a table when
or empty.
+ * @param eTag
+ * The etag of the entity.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify false
.
+ * @param xmlw
+ * The XMLStreamWriter
to write the entity to.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs creating or accessing the stream.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ */
+ protected static void writeSingleEntityToStream(final TableEntity entity, final boolean isTableEntry,
+ final XMLStreamWriter xmlw, final OperationContext opContext) throws XMLStreamException, StorageException {
+ // default is UTF8
+ xmlw.writeStartDocument("UTF-8", "1.0");
+
+ writeEntityToStream(entity, isTableEntry, xmlw, opContext);
+
+ // end doc
+ xmlw.writeEndDocument();
+ xmlw.flush();
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTableClient.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTableClient.java
new file mode 100644
index 0000000000000..b471594261c68
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTableClient.java
@@ -0,0 +1,1359 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.InvalidKeyException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
+import javax.xml.stream.XMLStreamException;
+
+import com.microsoft.windowsazure.services.core.storage.DoesServiceRequest;
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.ResultContinuation;
+import com.microsoft.windowsazure.services.core.storage.ResultContinuationType;
+import com.microsoft.windowsazure.services.core.storage.ResultSegment;
+import com.microsoft.windowsazure.services.core.storage.ServiceClient;
+import com.microsoft.windowsazure.services.core.storage.StorageCredentials;
+import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.LazySegmentedIterator;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.SegmentedStorageOperation;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;
+
+/**
+ * Provides a service client for accessing the Windows Azure Table service.
+ *
+ *     http://myaccount.table.core.windows.net
+ * For more information, see the MSDN topic Addressing Table Service Resources.
+ * String
+ * containing the table name.
+ */
+ private final EntityResolverjava.net.URI
that represents the Table service endpoint used to initialize the
+ * client.
+ */
+ public CloudTableClient(final URI baseUri) {
+ this(baseUri, null);
+ this.setTimeoutInMs(TableConstants.TABLE_DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ /**
+ * Initializes an instance of the {@link CloudTableClient} class using a Table service endpoint and
+ * storage account credentials.
+ *
+ * @param baseUri
+ * A java.net.URI
object that represents the Table service endpoint used to initialize the
+ * client.
+ * @param credentials
+ * A {@link StorageCredentials} object that represents the storage account credentials for access.
+ */
+ public CloudTableClient(final URI baseUri, StorageCredentials credentials) {
+ super(baseUri, credentials);
+ this.setTimeoutInMs(TableConstants.TABLE_DEFAULT_TIMEOUT_IN_MS);
+ }
+
+ /**
+ * Creates a table with the specified name in the storage service.
+ * String
object containing the name of the table to create.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table cannot be
+ * created, or already exists.
+ */
+ @DoesServiceRequest
+ public void createTable(final String tableName) throws StorageException {
+ this.createTable(tableName, null, null);
+ }
+
+ /**
+ * Creates a table with the specified name in the storage service, using the specified {@link TableRequestOptions}
+ * and {@link OperationContext}.
+ * String
object containing the name of the table to create.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table cannot be
+ * created, or already exists.
+ */
+ @DoesServiceRequest
+ public void createTable(final String tableName, TableRequestOptions options, OperationContext opContext)
+ throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(this);
+
+ Utility.assertNotNullOrEmpty("tableName", tableName);
+
+ final DynamicTableEntity tableEntry = new DynamicTableEntity();
+ tableEntry.getProperties().put(TableConstants.TABLE_NAME, new EntityProperty(tableName));
+
+ this.execute(TableConstants.TABLES_SERVICE_TABLES_NAME, TableOperation.insert(tableEntry), options, opContext);
+ }
+
+ /**
+ * Creates a table with the specified name in the storage service, if it does not already exist.
+ * String
object containing the name of the table to create.
+ *
+ * @return
+ * A value of true
if the operation created a new table, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table does not
+ * exist and cannot be created.
+ */
+ @DoesServiceRequest
+ public boolean createTableIfNotExists(final String tableName) throws StorageException {
+ return this.createTableIfNotExists(tableName, null, null);
+ }
+
+ /**
+ * Creates a table with the specified name in the storage service if it does not already exist, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
object containing the name of the table to create.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A value of true
if the operation created a new table, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table does not
+ * exist and cannot be created.
+ */
+ @DoesServiceRequest
+ public boolean createTableIfNotExists(final String tableName, TableRequestOptions options,
+ OperationContext opContext) throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(this);
+
+ Utility.assertNotNullOrEmpty("tableName", tableName);
+
+ if (this.doesTableExist(tableName, options, opContext)) {
+ return false;
+ }
+ else {
+ try {
+ this.createTable(tableName, options, opContext);
+ }
+ catch (StorageException ex) {
+ if (ex.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT
+ && StorageErrorCodeStrings.TABLE_ALREADY_EXISTS.equals(ex.getErrorCode())) {
+ return false;
+ }
+ else {
+ throw ex;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Deletes the table with the specified name in the storage service.
+ * String
object containing the name of the table to delete.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table deletion operation failed.
+ */
+ @DoesServiceRequest
+ public void deleteTable(final String tableName) throws StorageException {
+ this.deleteTable(tableName, null, null);
+ }
+
+ /**
+ * Deletes the table with the specified name in the storage service, using the specified {@link TableRequestOptions}
+ * and {@link OperationContext}.
+ * String
object containing the name of the table to delete.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table deletion operation failed.
+ */
+ @DoesServiceRequest
+ public void deleteTable(final String tableName, TableRequestOptions options, OperationContext opContext)
+ throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(this);
+
+ Utility.assertNotNullOrEmpty("tableName", tableName);
+ final DynamicTableEntity tableEntry = new DynamicTableEntity();
+ tableEntry.getProperties().put(TableConstants.TABLE_NAME, new EntityProperty(tableName));
+
+ final TableOperation delOp = new TableOperation(tableEntry, TableOperationType.DELETE);
+
+ final TableResult result = this.execute(TableConstants.TABLES_SERVICE_TABLES_NAME, delOp, options, opContext);
+
+ if (result.getHttpStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
+ return;
+ }
+ else {
+ throw new StorageException(StorageErrorCodeStrings.OUT_OF_RANGE_INPUT,
+ "Unexpected http status code received.", result.getHttpStatusCode(), null, null);
+ }
+ }
+
+ /**
+ * Deletes the table with the specified name in the storage service, if it exists.
+ * String
object containing the name of the table to delete.
+ *
+ * @return
+ * A value of true
if the operation deleted an existing table, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table deletion operation failed.
+ */
+ @DoesServiceRequest
+ public boolean deleteTableIfExists(final String tableName) throws StorageException {
+ return this.deleteTableIfExists(tableName, null, null);
+ }
+
+ /**
+ * Deletes the table with the specified name in the storage service, if it exists, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
object containing the name of the table to delete.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A value of true
if the operation deleted an existing table, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or because the table deletion operation failed.
+ */
+ @DoesServiceRequest
+ public boolean deleteTableIfExists(final String tableName, TableRequestOptions options, OperationContext opContext)
+ throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(this);
+
+ Utility.assertNotNullOrEmpty("tableName", tableName);
+
+ if (this.doesTableExist(tableName, options, opContext)) {
+ try {
+ this.deleteTable(tableName, options, opContext);
+ }
+ catch (StorageException ex) {
+ if (ex.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
+ && StorageErrorCodeStrings.RESOURCE_NOT_FOUND.equals(ex.getErrorCode())) {
+ return false;
+ }
+ else {
+ throw ex;
+ }
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Determines if a table with the specified name exists in the storage service.
+ * String
object containing the name of the table to find.
+ *
+ * @return
+ * A value of true
if the table exists, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service.
+ */
+ @DoesServiceRequest
+ public boolean doesTableExist(final String tableName) throws StorageException {
+ return this.doesTableExist(tableName, null, null);
+ }
+
+ /**
+ * Determines if a table with the specified name exists in the storage service, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
object containing the name of the table to find.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A value of true
if the table exists, otherwise false
.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service.
+ */
+ @DoesServiceRequest
+ public boolean doesTableExist(final String tableName, TableRequestOptions options, OperationContext opContext)
+ throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(this);
+
+ Utility.assertNotNullOrEmpty("tableName", tableName);
+
+ final TableResult result = this.execute(TableConstants.TABLES_SERVICE_TABLES_NAME,
+ TableOperation.retrieve(tableName /* Used As PK */, null/* Row Key */, DynamicTableEntity.class),
+ options, opContext);
+
+ if (result.getHttpStatusCode() == HttpURLConnection.HTTP_OK) {
+ return true;
+ }
+ else if (result.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+ return false;
+ }
+ else {
+ throw new StorageException(StorageErrorCodeStrings.OUT_OF_RANGE_INPUT,
+ "Unexpected http status code received.", result.getHttpStatusCode(), null, null);
+ }
+ }
+
+ /**
+ * Executes the specified batch operation on a table as an atomic operation. A batch operation may contain up to 100
+ * individual table operations, with the requirement that each operation entity must have same partition key. Only
+ * one retrieve operation is allowed per batch. Note that the total payload of a batch operation is limited to 4MB.
+ * String
containing the name of the table to execute the operations on.
+ * @param batch
+ * The {@link TableBatchOperation} object representing the operations to execute on the table.
+ *
+ * @return
+ * A java.util.ArrayList
of {@link TableResult} that contains the results, in order, of
+ * each {@link TableOperation} in the {@link TableBatchOperation} on the named table.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or the operation fails.
+ */
+ @DoesServiceRequest
+ public ArrayListString
containing the name of the table to execute the operations on.
+ * @param batch
+ * The {@link TableBatchOperation} object representing the operations to execute on the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A java.util.ArrayList
of {@link TableResult} that contains the results, in order, of
+ * each {@link TableOperation} in the {@link TableBatchOperation} on the named table.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or the operation fails.
+ */
+ @DoesServiceRequest
+ public ArrayListString
containing the name of the table to execute the operation on.
+ * @param operation
+ * The {@link TableOperation} object representing the operation to execute on the table.
+ *
+ * @return
+ * A {@link TableResult} containing the result of executing the {@link TableOperation} on the table.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or the operation fails.
+ */
+ @DoesServiceRequest
+ public TableResult execute(final String tableName, final TableOperation operation) throws StorageException {
+ return this.execute(tableName, operation, null, null);
+ }
+
+ /**
+ * Executes the operation on a table, using the specified {@link TableRequestOptions} and {@link OperationContext}.
+ * String
containing the name of the table to execute the operation on.
+ * @param operation
+ * The {@link TableOperation} object representing the operation to execute on the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A {@link TableResult} containing the result of executing the {@link TableOperation} on the table.
+ *
+ * @throws StorageException
+ * if an error occurs accessing the storage service, or the operation fails.
+ */
+ @DoesServiceRequest
+ public TableResult execute(final String tableName, final TableOperation operation,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ Utility.assertNotNull("operation", operation);
+ return operation.execute(this, tableName, options, opContext);
+ }
+
+ /**
+ * Executes a query, applying the specified {@link EntityResolver} to the result.
+ * R
.
+ *
+ * @return
+ * A collection implementing the Iterable
interface containing the projection into type
+ * R
of the results of executing the query.
+ */
+ @DoesServiceRequest
+ public R
.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A collection implementing the Iterable
interface containing the projection into type
+ * R
of the results of executing the query.
+ */
+ @DoesServiceRequest
+ @SuppressWarnings("unchecked")
+ public Iterable
interface specialized for type T of the results of
+ * executing the query.
+ */
+ @DoesServiceRequest
+ public null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A collection implementing the Iterable
interface specialized for type T of the results of
+ * executing the query.
+ */
+ @SuppressWarnings("unchecked")
+ @DoesServiceRequest
+ public executeSegmented
allows the query to be resumed after returning partial
+ * results, using information returned by the server in the {@link ResultSegment} object.
+ * R
.
+ * @param continuationToken
+ * A {@link ResultContinuation} object representing a continuation token from the server when the
+ * operation returns a partial result. Specify null
on the initial call. Call the
+ * {@link ResultSegment#getContinuationToken()} method on the result to obtain the
+ * {@link ResultContinuation} object to use in the next call to resume the query.
+ *
+ * @return
+ * A {@link ResultSegment} containing the projection into type R
of the results of executing
+ * the query.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the query is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ public executeSegmented
allows the query to be resumed after returning partial
+ * results, using information returned by the server in the {@link ResultSegment} object.
+ * R
.
+ * @param continuationToken
+ * A {@link ResultContinuation} object representing a continuation token from the server when the
+ * operation returns a partial result. Specify null
on the initial call. Call the
+ * {@link ResultSegment#getContinuationToken()} method on the result to obtain the
+ * {@link ResultContinuation} object to use in the next call to resume the query.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A {@link ResultSegment} containing the projection into type R
of the results of executing
+ * the query.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the query is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ @SuppressWarnings("unchecked")
+ public executeSegmented
allows the query to be resumed after returning partial
+ * results, using information returned by the server in the {@link ResultSegment} object.
+ * null
on the initial call. Call the
+ * {@link ResultSegment#getContinuationToken()} method on the result to obtain the
+ * {@link ResultContinuation} object to use in the next call to resume the query.
+ *
+ * @return
+ * A {@link ResultSegment} specialized for type T of the results of executing the query.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the query is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ public executeSegmented
allows the query to be resumed after returning partial
+ * results, using information returned by the server in the {@link ResultSegment} object.
+ * null
on the initial call. Call the
+ * {@link ResultSegment#getContinuationToken()} method on the result to obtain the
+ * {@link ResultContinuation} object to use in the next call to resume the query.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A {@link ResultSegment} specialized for type T of the results of executing the query.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the query is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ @SuppressWarnings("unchecked")
+ public Iterable
collection of the table names in the storage account.
+ */
+ @DoesServiceRequest
+ public IterableIterable
collection of the table names in the storage account that match the specified
+ * prefix.
+ */
+ @DoesServiceRequest
+ public Iterablenull
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An Iterable
collection of the table names in the storage account that match the specified
+ * prefix.
+ */
+ @DoesServiceRequest
+ public IterableString
objects containing table names in the storage account.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the operation is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ public ResultSegmentString
objects containing table names matching the prefix in the
+ * storage account.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the operation is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ public ResultSegmentnull
on the initial call. Call the
+ * {@link ResultSegment#getContinuationToken()} method on the result to obtain the
+ * {@link ResultContinuation} object to use in the next call to resume the query.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * A {@link ResultSegment} of String
objects containing table names in the storage account.
+ *
+ * @throws IOException
+ * if an IO error occurred during the operation.
+ * @throws URISyntaxException
+ * if the URI generated for the operation is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ @DoesServiceRequest
+ public ResultSegmentR
. Pass null
to return the results as the table entity type.
+ * @param continuationToken
+ * The {@link ResultContinuation} to pass with the operation to resume a query, if any. Pass
+ * null
for an initial query.
+ * @param taskReference
+ * A reference to the {@link StorageOperation} implementing the segmented operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ * @return
+ * A {@link ResultSegment} containing a collection of the query results specialized for the
+ * {@link TableEntity} or {@link EntityResolver} type returned by the query.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ * @throws IOException
+ * if an IO error occurs.
+ * @throws URISyntaxException
+ * if the URI generated for the query is invalid.
+ * @throws XMLStreamException
+ * if an error occurs accessing the XMLStreamReader
.
+ * @throws ParseException
+ * if an error occurs in parsing the response.
+ * @throws InstantiationException
+ * if an error occurs in object construction.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection on an object type.
+ * @throws InvalidKeyException
+ * if the key for an entity is invalid.
+ */
+ @SuppressWarnings("unchecked")
+ protected R
. Pass null
to return the results as the table entity
+ * type.
+ * @param continuationToken
+ * The {@link ResultContinuation} to pass with the operation to resume a query, if any. Pass
+ * null
for an initial query.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @return
+ * A {@link ResultSegment} containing a collection of the query results specialized for the
+ * {@link TableEntity} or {@link EntityResolver} type returned by the query.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ */
+ protected R
. Pass null
to return the results as the table entity
+ * type.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @return
+ * An instance of Iterable
specialized for the {@link TableEntity} or {@link EntityResolver}
+ * type returned by the query.
+ */
+ protected java.util.HashMap
containing a map of String
property names to
+ * {@link EntityProperty} data typed values to store in the new {@link DynamicTableEntity}.
+ */
+ public DynamicTableEntity(final HashMapjava.util.HashMap
containing the map of String
property names to
+ * {@link EntityProperty} data typed values for this {@link DynamicTableEntity} instance.
+ */
+ public HashMapjava.util.HashMap
of String
property names to {@link EntityProperty}
+ * data
+ * typed values to store in this {@link DynamicTableEntity} instance.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ */
+ @Override
+ public void readEntity(final HashMapjava.util.HashMap
containing the map of String
property names to
+ * {@link EntityProperty} data typed values to set in this {@link DynamicTableEntity} instance.
+ */
+ public void setProperties(final HashMapjava.util.HashMap
containing the map of String
property names to
+ * {@link EntityProperty} data typed values stored in this {@link DynamicTableEntity} instance.
+ * @throws StorageException
+ * if a Storage service error occurs.
+ */
+ @Override
+ public HashMapnull
or
+ * empty value parameter is matched as {@link EdmType#STRING}. Note that only the subset of EDM data types
+ * supported in Windows Azure Table storage is parsed, consisting of {@link #BINARY}, {@link #BOOLEAN},
+ * {@link #BYTE} , {@link #DATE_TIME}, {@link #DOUBLE}, {@link #GUID}, {@link #INT32}, {@link #INT64}, and
+ * {@link #STRING}. Any other type will cause an {@link IllegalArgumentException} to be thrown.
+ *
+ * @param value
+ * A String
containing the name of an EDM data type.
+ * @return
+ * The {@link EdmType} enumeration value matching the specified EDM data type.
+ * @throws IllegalArgumentException
+ * if an EDM data type not supported in Windows Azure Table storage is passed as an argument.
+ *
+ */
+ public static EdmType parse(final String value) {
+ if (value == null || value.length() == 0) {
+ return EdmType.STRING;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_DATETIME)) {
+ return EdmType.DATE_TIME;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_INT32)) {
+ return EdmType.INT32;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_BOOLEAN)) {
+ return EdmType.BOOLEAN;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_DOUBLE)) {
+ return EdmType.DOUBLE;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_INT64)) {
+ return EdmType.INT64;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_GUID)) {
+ return EdmType.GUID;
+ }
+ else if (value.equals(ODataConstants.EDMTYPE_BINARY)) {
+ return EdmType.BINARY;
+ }
+
+ throw new IllegalArgumentException("Invalid value for edmtype: ".concat(value));
+ }
+
+ /**
+ * Returns the name of the EDM data type corresponding to the enumeration value.
+ *
+ * @return
+ * A String
containing the name of the EDM data type.
+ */
+ @Override
+ public String toString() {
+ if (this == EdmType.BINARY) {
+ return ODataConstants.EDMTYPE_BINARY;
+ }
+ else if (this == EdmType.STRING) {
+ return Constants.EMPTY_STRING;
+ }
+ else if (this == EdmType.BOOLEAN) {
+ return ODataConstants.EDMTYPE_BOOLEAN;
+ }
+ else if (this == EdmType.DOUBLE) {
+ return ODataConstants.EDMTYPE_DOUBLE;
+ }
+ else if (this == EdmType.GUID) {
+ return ODataConstants.EDMTYPE_GUID;
+ }
+ else if (this == EdmType.INT32) {
+ return ODataConstants.EDMTYPE_INT32;
+ }
+ else if (this == EdmType.INT64) {
+ return ODataConstants.EDMTYPE_INT64;
+ }
+ else if (this == EdmType.DATE_TIME) {
+ return ODataConstants.EDMTYPE_DATETIME;
+ }
+ else {
+ // VNext : Update here if we add to supported edmtypes in the future.
+ return Constants.EMPTY_STRING;
+ }
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityProperty.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityProperty.java
new file mode 100644
index 0000000000000..b449c874abb87
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityProperty.java
@@ -0,0 +1,496 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.UUID;
+
+import com.microsoft.windowsazure.services.core.storage.Constants;
+import com.microsoft.windowsazure.services.core.storage.utils.Base64;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+
+/**
+ * A class which represents a single typed property value in a table entity. An {@link EntityProperty} stores the data
+ * type as an {@link EdmType}. The value, which may be null
for object types, but not for primitive types,
+ * is serialized and stored as a String
.
+ * setValue
method for
+ * supported value types. Each overloaded constructor or setValue
method sets the {@link EdmType} and
+ * serializes the value appropriately based on the parameter type.
+ * getValueAs
Type methods to deserialize an {@link EntityProperty} as the
+ * appropriate Java type. The method will throw a {@link ParseException} or {@link IllegalArgumentException} if the
+ * {@link EntityProperty} cannot be deserialized as the Java type.
+ */
+public final class EntityProperty {
+ private String value;
+ private EdmType edmType = EdmType.NULL;
+ private boolean isNull = false;
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a boolean
value.
+ *
+ * @param value
+ * The boolean
value of the entity property to set.
+ */
+ public EntityProperty(final boolean value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a byte[]
value.
+ *
+ * @param value
+ * The byte[]
value of the entity property to set.
+ */
+ public EntityProperty(final byte[] value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a Byte[]
.
+ *
+ * @param value
+ * The Byte[]
to set as the entity property value.
+ */
+ public EntityProperty(final Byte[] value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a Date
value.
+ *
+ * @param value
+ * The Date
to set as the entity property value.
+ */
+ public EntityProperty(final Date value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a double
value.
+ *
+ * @param value
+ * The double
value of the entity property to set.
+ */
+ public EntityProperty(final double value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from an int
value.
+ *
+ * @param value
+ * The int
value of the entity property to set.
+ */
+ public EntityProperty(final int value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a long
value.
+ *
+ * @param value
+ * The long
value of the entity property to set.
+ */
+ public EntityProperty(final long value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a String
value.
+ *
+ * @param value
+ * The String
to set as the entity property value.
+ */
+ public EntityProperty(final String value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Reserved for internal use. Constructs an {@link EntityProperty} instance from a String
value and a
+ * data type, and verifies that the value can be interpreted as the specified data type.
+ *
+ * @param value
+ * The String
representation of the value to construct.
+ * @param edmType
+ * The {@link EdmType} data type of the value to construct.
+ * @throws ParseException
+ * if the String
representation of the value cannot be interpreted as the data type.
+ */
+ protected EntityProperty(final String value, final EdmType edmType) throws ParseException {
+ this.edmType = edmType;
+ this.value = value;
+
+ // validate data is encoded correctly
+ if (edmType == EdmType.STRING) {
+ return;
+ }
+ else if (edmType == EdmType.BINARY) {
+ this.getValueAsByteArray();
+ }
+ else if (edmType == EdmType.BOOLEAN) {
+ this.getValueAsBoolean();
+ }
+ else if (edmType == EdmType.DOUBLE) {
+ this.getValueAsDouble();
+ }
+ else if (edmType == EdmType.GUID) {
+ this.getValueAsUUID();
+ }
+ else if (edmType == EdmType.INT32) {
+ this.getValueAsInteger();
+ }
+ else if (edmType == EdmType.INT64) {
+ this.getValueAsLong();
+ }
+ else if (edmType == EdmType.DATE_TIME) {
+ this.getValueAsDate();
+ }
+ }
+
+ /**
+ * Constructs an {@link EntityProperty} instance from a java.util.UUID
value.
+ *
+ * @param value
+ * The java.util.UUID
to set as the entity property value.
+ */
+ public EntityProperty(final UUID value) {
+ this.setValue(value);
+ }
+
+ /**
+ * Reserved for internal use. Constructs an {@link EntityProperty} instance as a null
value with the
+ * specified type.
+ *
+ * @param type
+ * The {@link EdmType} to set as the entity property type.
+ */
+ protected EntityProperty(EdmType type) {
+ this.value = null;
+ this.edmType = type;
+ this.isNull = true;
+ }
+
+ /**
+ * Gets the {@link EdmType} storage data type for the {@link EntityProperty}.
+ *
+ * @return
+ * The {@link EdmType} enumeration value for the data type of the {@link EntityProperty}.
+ */
+ public EdmType getEdmType() {
+ return this.edmType;
+ }
+
+ /**
+ * Gets a flag indicating that the {@link EntityProperty} value is null
.
+ *
+ * @return
+ * A boolean
flag indicating that the {@link EntityProperty} value is null
.
+ */
+ public boolean getIsNull() {
+ return this.isNull;
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a boolean
.
+ *
+ * @return
+ * A boolean
representation of the {@link EntityProperty} value.
+ *
+ * @throws IllegalArgumentException
+ * if the value is null
or cannot be parsed as a boolean
.
+ */
+ public boolean getValueAsBoolean() {
+ if (this.isNull) {
+ throw new IllegalArgumentException("EntityProperty cannot be set to null for value types.");
+ }
+ return Boolean.parseBoolean(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a byte
array.
+ *
+ * @return
+ * A byte[]
representation of the {@link EntityProperty} value, or null
.
+ */
+ public byte[] getValueAsByteArray() {
+ return this.isNull ? null : Base64.decode(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a Byte
array.
+ *
+ * @return
+ * A Byte[]
representation of the {@link EntityProperty} value, or null
.
+ */
+ public Byte[] getValueAsByteObjectArray() {
+ return this.isNull ? null : Base64.decodeAsByteObjectArray(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a Date
.
+ *
+ * @return
+ * A Date
representation of the {@link EntityProperty} value, or null
.
+ *
+ * @throws IllegalArgumentException
+ * if the value is not null
and cannot be parsed as a Date
.
+ */
+ public Date getValueAsDate() {
+ if (this.isNull) {
+ return null;
+ }
+
+ return Utility.parseDate(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a double
.
+ *
+ * @return
+ * A double
representation of the {@link EntityProperty} value.
+ *
+ * @throws IllegalArgumentException
+ * if the value is null
or cannot be parsed as a double
.
+ */
+ public double getValueAsDouble() {
+ if (this.isNull) {
+ throw new IllegalArgumentException("EntityProperty cannot be set to null for value types.");
+ }
+ return Double.parseDouble(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as an int
.
+ *
+ * @return
+ * An int
representation of the {@link EntityProperty} value.
+ *
+ * @throws IllegalArgumentException
+ * if the value is null
or cannot be parsed as an int
.
+ */
+ public int getValueAsInteger() {
+ if (this.isNull) {
+ throw new IllegalArgumentException("EntityProperty cannot be set to null for value types.");
+ }
+ return Integer.parseInt(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a long
.
+ *
+ * @return
+ * A long
representation of the {@link EntityProperty} value.
+ *
+ * @throws IllegalArgumentException
+ * if the value is null
or cannot be parsed as a long
.
+ */
+ public long getValueAsLong() {
+ if (this.isNull) {
+ throw new IllegalArgumentException("EntityProperty cannot be set to null for value types.");
+ }
+ return Long.parseLong(this.value);
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a String
.
+ *
+ * @return
+ * A String
representation of the {@link EntityProperty} value, or null
.
+ */
+ public String getValueAsString() {
+ return this.isNull ? null : this.value;
+ }
+
+ /**
+ * Gets the value of this {@link EntityProperty} as a java.util.UUID
.
+ *
+ * @return
+ * A java.util.UUID
representation of the {@link EntityProperty} value, or null
.
+ *
+ * @throws IllegalArgumentException
+ * if the value cannot be parsed as a java.util.UUID
.
+ */
+ public UUID getValueAsUUID() {
+ return this.isNull ? null : UUID.fromString(this.value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized boolean
value.
+ *
+ * @param value
+ * The boolean
value to set as the {@link EntityProperty} value.
+ */
+ public synchronized final void setValue(final boolean value) {
+ this.edmType = EdmType.BOOLEAN;
+ this.isNull = false;
+ this.value = value ? Constants.TRUE : Constants.FALSE;
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized byte[]
value.
+ *
+ * @param value
+ * The byte[]
value to set as the {@link EntityProperty} value. This value may be
+ * null
.
+ */
+ public synchronized final void setValue(final byte[] value) {
+ this.edmType = EdmType.BINARY;
+ if (value == null) {
+ this.value = null;
+ this.isNull = true;
+ return;
+ }
+ else {
+ this.isNull = false;
+ }
+
+ this.value = Base64.encode(value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized Byte[]
value.
+ *
+ * @param value
+ * The Byte[]
value to set as the {@link EntityProperty} value. This value may be
+ * null
.
+ */
+ public synchronized final void setValue(final Byte[] value) {
+ this.edmType = EdmType.BINARY;
+ if (value == null) {
+ this.value = null;
+ this.isNull = true;
+ return;
+ }
+ else {
+ this.isNull = false;
+ }
+
+ this.value = Base64.encode(value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized Date
value.
+ *
+ * @param value
+ * The Date
value to set as the {@link EntityProperty} value. This value may be
+ * null
.
+ */
+ public synchronized final void setValue(final Date value) {
+ this.edmType = EdmType.DATE_TIME;
+
+ if (value == null) {
+ this.value = null;
+ this.isNull = true;
+ return;
+ }
+ else {
+ this.isNull = false;
+ }
+
+ this.value = Utility.getTimeByZoneAndFormat(value, Utility.UTC_ZONE, Utility.ISO8061_LONG_PATTERN);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized double
value.
+ *
+ * @param value
+ * The double
value to set as the {@link EntityProperty} value.
+ */
+ public synchronized final void setValue(final double value) {
+ this.edmType = EdmType.DOUBLE;
+ this.isNull = false;
+ this.value = Double.toString(value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized int
value.
+ *
+ * @param value
+ * The int
value to set as the {@link EntityProperty} value.
+ */
+ public synchronized final void setValue(final int value) {
+ this.edmType = EdmType.INT32;
+ this.isNull = false;
+ this.value = Integer.toString(value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized long
value.
+ *
+ * @param value
+ * The long
value to set as the {@link EntityProperty} value.
+ */
+ public synchronized final void setValue(final long value) {
+ this.edmType = EdmType.INT64;
+ this.isNull = false;
+ this.value = Long.toString(value);
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the String
value.
+ *
+ * @param value
+ * The String
value to set as the {@link EntityProperty} value. This value may be
+ * null
.
+ */
+ public synchronized final void setValue(final String value) {
+ this.edmType = EdmType.STRING;
+ if (value == null) {
+ this.value = null;
+ this.isNull = true;
+ return;
+ }
+ else {
+ this.isNull = false;
+ }
+
+ this.value = value;
+ }
+
+ /**
+ * Sets this {@link EntityProperty} using the serialized java.util.UUID
value.
+ *
+ * @param value
+ * The java.util.UUID
value to set as the {@link EntityProperty} value.
+ * This value may be null
.
+ */
+ public synchronized final void setValue(final UUID value) {
+ this.edmType = EdmType.GUID;
+ if (value == null) {
+ this.value = null;
+ this.isNull = true;
+ return;
+ }
+ else {
+ this.isNull = false;
+ }
+
+ this.value = value.toString();
+ }
+
+ /**
+ * Reserved for internal use. Sets the null value flag to the specified boolean
value.
+ *
+ * @param isNull
+ * The boolean
value to set in the null value flag.
+ */
+ protected void setIsNull(final boolean isNull) {
+ this.isNull = isNull;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityResolver.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityResolver.java
new file mode 100644
index 0000000000000..1f7d95d3a812a
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/EntityResolver.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.Date;
+import java.util.HashMap;
+
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+
+/**
+ * An interface to perform client side projection on a retrieved entity. An {@link EntityResolver} instance must
+ * implement a resolve
method projecting the entity data represented by the parameters passed in as a new
+ * instance of the type specified by the type parameter.
+ * String
for the
+ * CustomerName property of each entity. The result of this projection will be a collection of
+ * String
s containing each customer name.
+ *
+ * @param T
containing a projection of the specified
+ * table entity data.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity.
+ * @param rowKey
+ * A String
containing the RowKey value for the entity.
+ * @param timeStamp
+ * A Date
containing the Timestamp value for the entity.
+ * @param properties
+ * The java.util.HashMap
of String
property names to {@link EntityProperty}
+ * data type and value pairs representing the table entity data.
+ * @param etag
+ * A String
containing the Etag for the entity.
+ * @return
+ * A reference to an object instance of type T
constructed as a projection of the table entity
+ * parameters.
+ * @throws StorageException
+ * if an error occurs during the operation.
+ */
+ T resolve(String partitionKey, String rowKey, Date timeStamp, HashMap@Ignore
annotation to methods in a class implementing {@link TableEntity} to force them to be ignored
+ * during reflection-based serialization and deserialization. See the documentation for {@link TableServiceEntity} for
+ * more information on using reflection-based serialization and deserialization.
+ *
+ * @see StoreAs
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface Ignore {
+ // No attributes
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHeader.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHeader.java
new file mode 100644
index 0000000000000..bffc95cdd4276
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHeader.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+/**
+ * Reserved for internal use. A class that represents a given MIME Header.
+ */
+class MimeHeader {
+ protected String boundary;
+ protected String contentType;
+ protected String contentTransferEncoding;
+ protected String subBoundary;
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHelper.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHelper.java
new file mode 100644
index 0000000000000..143a4022e3740
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimeHelper.java
@@ -0,0 +1,510 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import com.microsoft.windowsazure.services.core.storage.Constants;
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+
+/**
+ * Reserved for internal use. A class used to read and write MIME requests and responses.
+ */
+class MimeHelper {
+ /**
+ * Reserved for internal use. A static factory method that generates a {@link StorageException} for invalid MIME
+ * responses.
+ *
+ * @return
+ * The {@link StorageException} for the invalid MIME response.
+ */
+ protected static StorageException generateMimeParseException() {
+ return new StorageException(StorageErrorCodeStrings.OUT_OF_RANGE_INPUT, "Invalid MIME response received.",
+ Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
+ }
+
+ /**
+ * Reserved for internal use. Returns the HTTP verb for a table operation.
+ *
+ * @param operation
+ * The {@link TableOperation} instance to get the HTTP verb for.
+ * @return
+ * A String
containing the HTTP verb to use with the operation.
+ */
+ protected static String getHttpVerbForOperation(final TableOperation operation) {
+ if (operation.getOperationType() == TableOperationType.INSERT) {
+ return "POST";
+ }
+ else if (operation.getOperationType() == TableOperationType.DELETE) {
+ return "DELETE";
+ }
+ else if (operation.getOperationType() == TableOperationType.MERGE
+ || operation.getOperationType() == TableOperationType.INSERT_OR_MERGE) {
+ return "MERGE";
+ }
+ else if (operation.getOperationType() == TableOperationType.REPLACE
+ || operation.getOperationType() == TableOperationType.INSERT_OR_REPLACE) {
+ return "PUT";
+ }
+ else if (operation.getOperationType() == TableOperationType.RETRIEVE) {
+ return "GET";
+ }
+ else {
+ throw new IllegalArgumentException("Unknown table operation");
+ }
+ }
+
+ /**
+ * Reserved for internal use. Returns the next non-blank line from the {@link BufferedReader}.
+ *
+ * @param reader
+ * The {@link BufferedReader} to read lines from.
+ * @return
+ * A String
containing the next non-blank line from the {@link BufferedReader}, or
+ * null
.
+ * @throws IOException
+ * if an error occurs reading from the {@link BufferedReader}.
+ */
+ protected static String getNextLineSkippingBlankLines(final BufferedReader reader) throws IOException {
+ String tString = null;
+ do {
+ tString = reader.readLine();
+ } while (tString != null && tString.length() == 0);
+
+ return tString;
+ }
+
+ /**
+ * Reserved for internal use. Reads the response stream from a batch operation into an ArrayList
of
+ * {@link MimePart} objects.
+ *
+ * @param inStream
+ * An {@link InputStream} containing the operation response stream.
+ * @param expectedBundaryName
+ * A String
containing the MIME part boundary string.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @return
+ * An ArrayList
of {@link MimePart} objects parsed from the input stream.
+ * @throws IOException
+ * if an error occurs accessing the input stream.
+ * @throws StorageException
+ * if an error occurs parsing the input stream.
+ */
+ protected static ArrayListnull
to
+ * safely ignore operation context.
+ * @return
+ * A {@link MimeHeader} constructed by parsing the MIME header data from the {@link BufferedReader}.
+ * @throws IOException
+ * if an error occurs accessing the input stream.
+ * @throws StorageException
+ * if an error occurs parsing the input stream.
+ */
+ protected static MimeHeader readMimeHeader(final BufferedReader reader, final OperationContext opContext)
+ throws IOException, StorageException {
+ final MimeHeader retHeader = new MimeHeader();
+ reader.mark(1024 * 1024);
+
+ // First thing is separator
+ retHeader.boundary = getNextLineSkippingBlankLines(reader);
+ if (retHeader.boundary.endsWith("--")) {
+ return null;
+ }
+ if (!retHeader.boundary.startsWith("--")) {
+ reader.reset();
+ return null;
+ }
+
+ for (int m = 0; m < 2; m++) {
+ final String tempString = reader.readLine();
+ if (tempString.length() == 0) {
+ break;
+ }
+
+ if (tempString.startsWith("Content-Type:")) {
+ final String[] headerVals = tempString.split("Content-Type: ");
+ if (headerVals == null || headerVals.length != 2) {
+ throw generateMimeParseException();
+ }
+ retHeader.contentType = headerVals[1];
+ }
+ else if (tempString.startsWith("Content-Transfer-Encoding:")) {
+ final String[] headerVals = tempString.split("Content-Transfer-Encoding: ");
+ if (headerVals == null || headerVals.length != 2) {
+ throw generateMimeParseException();
+ }
+ retHeader.contentTransferEncoding = headerVals[1];
+ }
+ else {
+ throw generateMimeParseException();
+ }
+ }
+
+ // Validate headers
+ if (Utility.isNullOrEmpty(retHeader.boundary) || retHeader.contentType == null) {
+ throw generateMimeParseException();
+ }
+
+ if (retHeader.contentType.startsWith("multipart/mixed; boundary=")) {
+ final String[] headerVals = retHeader.contentType.split("multipart/mixed; boundary=");
+ if (headerVals == null || headerVals.length != 2) {
+ throw generateMimeParseException();
+ }
+ retHeader.subBoundary = "--".concat(headerVals[1]);
+ }
+ else if (!retHeader.contentType.equals("application/http")) {
+ throw generateMimeParseException();
+ }
+
+ if (retHeader.contentTransferEncoding != null && !retHeader.contentTransferEncoding.equals("binary")) {
+ throw generateMimeParseException();
+ }
+
+ return retHeader;
+ }
+
+ // Returns at start of next mime boundary header
+ /**
+ * Reserved for internal use. A static factory method that generates a {@link MimePart} containing the next MIME
+ * part read from the {@link BufferedReader}.
+ * The {@link BufferedReader} is left positioned at the start of the next MIME boundary header.
+ *
+ * @param reader
+ * The {@link BufferedReader} containing the response stream to parse.
+ * @param boundary
+ * A String
containing the MIME part boundary string.
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @return
+ * A {@link MimePart} constructed by parsing the next MIME part data from the {@link BufferedReader}.
+ * @throws IOException
+ * if an error occured accessing the input stream.
+ * @throws StorageException
+ * if an error occured parsing the input stream.
+ */
+ protected static MimePart readMimePart(final BufferedReader reader, final String boundary,
+ final OperationContext opContext) throws IOException, StorageException {
+ final MimePart retPart = new MimePart();
+ // Read HttpStatus code
+ String tempStr = getNextLineSkippingBlankLines(reader);
+ if (!tempStr.startsWith("HTTP/1.1 ")) {
+ throw generateMimeParseException();
+ }
+
+ final String[] headerVals = tempStr.split(" ");
+
+ if (headerVals.length < 3) {
+ throw generateMimeParseException();
+ }
+
+ retPart.httpStatusCode = Integer.parseInt(headerVals[1]);
+ // "HTTP/1.1 XXX ".length() => 13
+ retPart.httpStatusMessage = tempStr.substring(13);
+
+ // Read headers
+ tempStr = reader.readLine();
+ while (tempStr != null && tempStr.length() > 0) {
+ final String[] headerParts = tempStr.split(": ");
+ if (headerParts.length < 2) {
+ throw generateMimeParseException();
+ }
+
+ retPart.headers.put(headerParts[0], headerParts[1]);
+ tempStr = reader.readLine();
+ }
+
+ // Store xml payload
+ reader.mark(1024 * 1024);
+ tempStr = getNextLineSkippingBlankLines(reader);
+
+ if (tempStr == null) {
+ throw generateMimeParseException();
+ }
+
+ // empty body
+ if (tempStr.startsWith(boundary)) {
+ reader.reset();
+ retPart.payload = Constants.EMPTY_STRING;
+ return retPart;
+ }
+ else if (!tempStr.startsWith("Performing Entity Group
+ * TransactionsString
containing the name of the table to apply each operation to.
+ * @param batch
+ * A {@link TableBatchOperation} containing the operations to write to the output stream
+ * @param batchID
+ * A String
containing the identifier to use as the MIME boundary for the batch request.
+ * @param changeSet
+ * A String
containing the identifier to use as the MIME boundary for operations within the
+ * batch.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @throws IOException
+ * if an IO error occurs.
+ * @throws URISyntaxException
+ * if an invalid URI is used.
+ * @throws StorageException
+ * if an error occurs accessing the Storage service.
+ * @throws XMLStreamException
+ * if an error occurs accessing the stream.
+ */
+ protected static void writeBatchToStream(final OutputStream outStream, final String tableName,
+ final TableBatchOperation batch, final String batchID, final String changeSet,
+ final OperationContext opContext) throws IOException, URISyntaxException, StorageException,
+ XMLStreamException {
+ final OutputStreamWriter outWriter = new OutputStreamWriter(outStream, "UTF8");
+
+ int contentID = 0;
+ boolean inChangeSet = false;
+ for (final TableOperation op : batch) {
+ if (op.getOperationType() == TableOperationType.RETRIEVE) {
+ final QueryTableOperation qOp = (QueryTableOperation) op;
+
+ if (inChangeSet) {
+ inChangeSet = false;
+ // Write Boundary end.
+ MimeHelper.writeMIMEBoundaryClosure(outWriter, changeSet);
+ outWriter.write("\r\n");
+ }
+
+ // Write MIME Header
+ MimeHelper.writeMIMEBoundary(outWriter, batchID);
+ outWriter.write("Content-Type: application/http\r\n");
+ outWriter.write("Content-Transfer-Encoding: binary\r\n\r\n");
+
+ outWriter.write(String.format("%s %s HTTP/1.1\r\n", getHttpVerbForOperation(op),
+ qOp.generateRequestIdentityWithTable(tableName)));
+
+ outWriter.write("Host: host\r\n\r\n");
+ }
+ else {
+ if (!inChangeSet) {
+ inChangeSet = true;
+ // New batch mime part
+ MimeHelper.writeMIMEBoundary(outWriter, batchID);
+ MimeHelper.writeMIMEContentType(outWriter, changeSet);
+ outWriter.write("\r\n");
+ }
+
+ // New mime part for changeset
+ MimeHelper.writeMIMEBoundary(outWriter, changeSet);
+
+ // Write Headers
+ outWriter.write("Content-Type: application/http\r\n");
+ outWriter.write("Content-Transfer-Encoding: binary\r\n\r\n");
+
+ outWriter.write(String.format("%s %s HTTP/1.1\r\n", getHttpVerbForOperation(op),
+ op.generateRequestIdentityWithTable(tableName)));
+
+ outWriter.write(String.format("Content-ID: %s\r\n", Integer.toString(contentID)));
+
+ if (op.getOperationType() != TableOperationType.INSERT
+ && op.getOperationType() != TableOperationType.INSERT_OR_MERGE
+ && op.getOperationType() != TableOperationType.INSERT_OR_REPLACE) {
+ outWriter.write(String.format("If-Match: %s\r\n", op.getEntity().getEtag()));
+ }
+
+ if (op.getOperationType() == TableOperationType.DELETE) {
+ // empty body
+ outWriter.write("\r\n");
+ }
+ else {
+ outWriter.write("Content-Type: application/atom+xml;type=entry\r\n");
+ final String opString = writeStringForOperation(op, opContext);
+ outWriter.write(String.format("Content-Length: %s\r\n\r\n",
+ Integer.toString(opString.getBytes("UTF-8").length)));
+ outWriter.write(opString);
+ }
+ contentID = contentID + 1;
+ }
+ }
+
+ if (inChangeSet) {
+ MimeHelper.writeMIMEBoundaryClosure(outWriter, changeSet);
+ }
+ MimeHelper.writeMIMEBoundaryClosure(outWriter, batchID);
+
+ outWriter.flush();
+ }
+
+ /**
+ * Reserved for internal use. Writes a MIME part boundary to the output stream.
+ *
+ * @param outWriter
+ * The {@link OutputStreamWriter} to write the MIME part boundary to.
+ * @param boundaryID
+ * The String
containing the MIME part boundary string.
+ * @throws IOException
+ * if an error occurs writing to the output stream.
+ */
+ protected static void writeMIMEBoundary(final OutputStreamWriter outWriter, final String boundaryID)
+ throws IOException {
+ outWriter.write(String.format("--%s\r\n", boundaryID));
+ }
+
+ /**
+ * Reserved for internal use. Writes a MIME part boundary closure to the output stream.
+ *
+ * @param outWriter
+ * The {@link OutputStreamWriter} to write the MIME part boundary closure to.
+ * @param boundaryID
+ * The String
containing the MIME part boundary string.
+ * @throws IOException
+ * if an error occurs writing to the output stream.
+ */
+ protected static void writeMIMEBoundaryClosure(final OutputStreamWriter outWriter, final String boundaryID)
+ throws IOException {
+ outWriter.write(String.format("--%s--\r\n", boundaryID));
+ }
+
+ /**
+ * Reserved for internal use. Writes a MIME content type string to the output stream.
+ *
+ * @param outWriter
+ * The {@link OutputStreamWriter} to write the MIME content type string to.
+ * @param boundaryID
+ * The String
containing the MIME part boundary string.
+ * @throws IOException
+ * if an error occurs writing to the output stream.
+ */
+ protected static void writeMIMEContentType(final OutputStreamWriter outWriter, final String boundaryName)
+ throws IOException {
+ outWriter.write(String.format("Content-Type: multipart/mixed; boundary=%s\r\n", boundaryName));
+ }
+
+ /**
+ * Reserved for internal use. Generates a String
containing the entity associated with an operation in
+ * AtomPub format.
+ *
+ * @param operation
+ * A {@link TableOperation} containing the entity to write to the returned String
.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ * @return
+ * A String
containing the entity associated with the operation in AtomPub format
+ * @throws StorageException
+ * if a Storage error occurs.
+ * @throws XMLStreamException
+ * if an error occurs creating or writing to the output string.
+ */
+ protected static String writeStringForOperation(final TableOperation operation, final OperationContext opContext)
+ throws StorageException, XMLStreamException {
+ final StringWriter outWriter = new StringWriter();
+ final XMLOutputFactory xmlOutFactoryInst = XMLOutputFactory.newInstance();
+ final XMLStreamWriter xmlw = xmlOutFactoryInst.createXMLStreamWriter(outWriter);
+
+ AtomPubParser.writeSingleEntityToStream(operation.getEntity(), false, xmlw, opContext);
+ outWriter.write("\r\n");
+
+ return outWriter.toString();
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimePart.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimePart.java
new file mode 100644
index 0000000000000..254581502e2b8
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/MimePart.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.HashMap;
+
+/**
+ * Reserved for internal use. A class that represents a given MIME Part.
+ */
+class MimePart {
+ protected int httpStatusCode = -1;
+ protected String httpStatusMessage;
+ protected HashMapString
representation of the Atom namespace.
+ */
+ public static final String ATOM_NS = "http://www.w3.org/2005/Atom";
+
+ /**
+ * The String
representation of the OData Data namespace.
+ */
+ public static final String DATA_SERVICES_NS = "http://schemas.microsoft.com/ado/2007/08/dataservices";
+
+ /**
+ * The String
representation of the OData Metadata namespace.
+ */
+ public static final String DATA_SERVICES_METADATA_NS = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
+
+ /**
+ * The String
representation of the Atom namespace in brackets.
+ */
+ public static final String BRACKETED_ATOM_NS = "{" + ATOM_NS + "}"; // default
+
+ /**
+ * The String
representation of the OData Data namespace in brackets.
+ */
+ public static final String BRACKETED_DATA_SERVICES_NS = "{" + DATA_SERVICES_NS + "}"; // d:
+
+ /**
+ * The String
representation of the OData Metadata namespace in brackets.
+ */
+ public static final String BRACKETED_DATA_SERVICES_METADATA_NS = "{" + DATA_SERVICES_METADATA_NS + "}"; // m:
+
+ /**
+ * The String
representation of the Atom Entry feed element name.
+ */
+ public static final String FEED = "feed";
+
+ /**
+ * The String
representation of the Atom Entry title element name.
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * The String
representation of the Atom Entry id element name.
+ */
+ public static final String ID = "id";
+
+ /**
+ * The String
representation of the Atom Entry updated element name.
+ */
+ public static final String UPDATED = "updated";
+
+ /**
+ * The String
representation of the Atom Entry link element name.
+ */
+ public static final String LINK = "link";
+
+ /**
+ * The String
representation of the Atom Entry author element name.
+ */
+ public static final String AUTHOR = "author";
+
+ /**
+ * The String
representation of the Atom Entry name element name.
+ */
+ public static final String NAME = "name";
+
+ /**
+ * The String
representation of the Atom Entry entry element name.
+ */
+ public static final String ENTRY = "entry";
+
+ /**
+ * The String
representation of the Atom Entry category element name.
+ */
+ public static final String CATEGORY = "category";
+
+ /**
+ * The String
representation of the Atom Entry content element name.
+ */
+ public static final String CONTENT = "content";
+
+ /**
+ * The String
representation of the OData Metadata properties element name.
+ */
+ public static final String PROPERTIES = "properties";
+
+ /**
+ * The String
representation of the Atom Entry etag element name.
+ */
+ public static final String ETAG = "etag";
+
+ /**
+ * The String
representation of the type attribute name.
+ */
+ public static final String TYPE = "type";
+
+ /**
+ * The String
representation of the term element name.
+ */
+ public static final String TERM = "term";
+
+ /**
+ * The String
representation of scheme.
+ */
+ public static final String SCHEME = "scheme";
+
+ /**
+ * The String
representation of href.
+ */
+ public static final String HREF = "href";
+
+ /**
+ * The String
representation of rel.
+ */
+ public static final String REL = "rel";
+
+ /**
+ * The String
representation of the null attribute name.
+ */
+ public static final String NULL = "null";
+
+ /**
+ * The String
representation of the content type attribute value to send.
+ */
+ public static final String ODATA_CONTENT_TYPE = "application/xml";
+
+ // Odata edm types
+
+ /**
+ * The String
representation of the Edm.DateTime metadata type attribute value.
+ */
+ public static final String EDMTYPE_DATETIME = "Edm.DateTime";
+
+ /**
+ * The String
representation of the Edm.Binary metadata type attribute value.
+ */
+ public static final String EDMTYPE_BINARY = "Edm.Binary";
+
+ /**
+ * The String
representation of the Edm.Boolean metadata type attribute value.
+ */
+ public static final String EDMTYPE_BOOLEAN = "Edm.Boolean";
+
+ /**
+ * The String
representation of the Edm.Double metadata type attribute value.
+ */
+ public static final String EDMTYPE_DOUBLE = "Edm.Double";
+
+ /**
+ * The String
representation of the Edm.Guid metadata type attribute value.
+ */
+ public static final String EDMTYPE_GUID = "Edm.Guid";
+
+ /**
+ * The String
representation of the Edm.Int32 metadata type attribute value.
+ */
+ public static final String EDMTYPE_INT32 = "Edm.Int32";
+
+ /**
+ * The String
representation of the Edm.Int64 metadata type attribute value.
+ */
+ public static final String EDMTYPE_INT64 = "Edm.Int64";
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/ODataPayload.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/ODataPayload.java
new file mode 100644
index 0000000000000..9f0f5c573becb
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/ODataPayload.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.ArrayList;
+
+/**
+ * Reserved for internal use. A class that represents an OData payload and resulting entities.
+ */
+class ODataPayloadgetPropertyName
+ * and setPropertyName
, with a common type for the getter return value and the
+ * setter parameter, and stores the methods and the property name for each pair found in a map for use in
+ * serializing and deserializing entity data.
+ *
+ * @param clazzType
+ * The class type to check for matching getter and setter methods with a common return and parameter
+ * type, respectively.
+ */
+ protected static HashMaptrue
if this property is accessible
+ * through reflection.
+ *
+ * @return
+ */
+ protected boolean shouldProcess() {
+ if (Utility.isNullOrEmpty(this.name) || this.getter == null || this.getter.isAnnotationPresent(Ignore.class)
+ || this.setter == null || this.setter.isAnnotationPresent(Ignore.class)
+ || (!this.getter.getReturnType().equals(this.setter.getParameterTypes()[0]))) {
+ return false;
+ }
+
+ // TODO add logging
+ // System.out.println("Valid property " + this.name + " Storing as " + this.effectiveName);
+ return true;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java
new file mode 100644
index 0000000000000..52dbf5ecdccb3
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java
@@ -0,0 +1,268 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.text.ParseException;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
+import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;
+
+/**
+ * A class that extends {@link TableOperation} to implement a query to retrieve a single table entity. To execute a
+ * {@link QueryTableOperation} instance, call the execute
method on a {@link CloudTableClient} instance.
+ * This operation can be executed directly or as part of a {@link TableBatchOperation}. If the
+ * {@link QueryTableOperation} returns an entity result, it is stored in the corresponding {@link TableResult} returned
+ * by the execute
method.
+ */
+public class QueryTableOperation extends TableOperation {
+ private EntityResolver> resolver;
+
+ private Class extends TableEntity> clazzType;
+
+ private String partitionKey;
+
+ private String rowKey;
+
+ /**
+ * Default constructor.
+ */
+ protected QueryTableOperation() {
+ super(null, TableOperationType.RETRIEVE);
+ }
+
+ /**
+ * Constructs a {@link QueryTableOperation} instance to retrieve a single table entity with the specified partition
+ * key and row key.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity.
+ * @param rowKey
+ * A String
containing the RowKey value for the entity.
+ */
+ QueryTableOperation(final String partitionKey, final String rowKey) {
+ super(null, TableOperationType.RETRIEVE);
+ Utility.assertNotNullOrEmpty("partitionKey", partitionKey);
+ this.partitionKey = partitionKey;
+ this.rowKey = rowKey;
+ }
+
+ /**
+ * Gets the PartitionKey value for the entity to retrieve.
+ *
+ * @return
+ * A String
containing the PartitionKey value for the entity.
+ */
+ public String getPartitionKey() {
+ return this.partitionKey;
+ }
+
+ /**
+ * Gets the resolver to project the entity retrieved as a particular type.
+ *
+ * @return
+ * The {@link EntityResolver} instance.
+ */
+ public EntityResolver> getResolver() {
+ return this.resolver;
+ }
+
+ /**
+ * Gets the RowKey value for the entity to retrieve.
+ *
+ * @return
+ * A String
containing the RowKey value for the entity.
+ */
+ public String getRowKey() {
+ return this.rowKey;
+ }
+
+ /**
+ * Reserved for internal use. Gets the class type of the entity returned by the query.
+ *
+ * @return
+ * The java.lang.Class
implementing {@link TableEntity} that represents the entity type for the
+ * query.
+ */
+ protected Class extends TableEntity> getClazzType() {
+ return this.clazzType;
+ }
+
+ /**
+ * Reserved for internal use. Parses the query table operation response into a {@link TableResult} to return.
+ *
+ * @param xmlr
+ * An XMLStreamReader
containing the response to the query operation.
+ * @param httpStatusCode
+ * The HTTP status code returned from the operation request.
+ * @param etagFromHeader
+ * The String
containing the Etag returned with the operation response.
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation.
+ *
+ * @return
+ * The {@link TableResult} representing the result of the query operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs accessing the XMLStreamReader
.
+ * @throws ParseException
+ * if an error occurs in parsing the response.
+ * @throws InstantiationException
+ * if an error occurs in object construction.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection on an object type.
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ @Override
+ protected TableResult parseResponse(final XMLStreamReader xmlr, final int httpStatusCode,
+ final String etagFromHeader, final OperationContext opContext) throws XMLStreamException, ParseException,
+ InstantiationException, IllegalAccessException, StorageException {
+ return AtomPubParser.parseSingleOpResponse(xmlr, httpStatusCode, this.getClazzType(), this.getResolver(),
+ opContext);
+ }
+
+ /**
+ * Reserved for internal use. Performs a retrieve operation on the specified table, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
containing the name of the table to query.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the query operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ protected TableResult performRetrieve(final CloudTableClient client, final String tableName,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ final boolean isTableEntry = TableConstants.TABLES_SERVICE_TABLES_NAME.equals(tableName);
+ if (this.getClazzType() != null) {
+ Utility.checkNullaryCtor(this.getClazzType());
+ }
+ else {
+ Utility.assertNotNull("Query requires a valid class type or resolver.", this.getResolver());
+ }
+
+ final StorageOperationjava.lang.Class
implementing {@link TableEntity} that represents the entity type for
+ * the query.
+ */
+ protected void setClazzType(final Class extends TableEntity> clazzType) {
+ Utility.assertNotNull("clazzType", clazzType);
+ Utility.checkNullaryCtor(clazzType);
+ this.clazzType = clazzType;
+ }
+
+ /**
+ * Reserved for internal use. Sets the PartitionKey value for the entity to retrieve.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity.
+ */
+ protected void setPartitionKey(final String partitionKey) {
+ this.partitionKey = partitionKey;
+ }
+
+ /**
+ * Reserved for internal use. Sets the resolver to project the entity retrieved as a particular type.
+ *
+ * @param resolver
+ * The {@link EntityResolver} instance to use.
+ */
+ protected void setResolver(final EntityResolver> resolver) {
+ Utility.assertNotNull("Query requires a valid class type or resolver.", resolver);
+ this.resolver = resolver;
+ }
+
+ /**
+ * Reserved for internal use. Sets the RowKey value for the entity to retrieve.
+ *
+ * @param rowKey
+ * A String
containing the RowKey value for the entity.
+ */
+ protected void setRowKey(final String rowKey) {
+ this.rowKey = rowKey;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/StoreAs.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/StoreAs.java
new file mode 100644
index 0000000000000..7918530c85f34
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/StoreAs.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to override the name a property is serialized and deserialized with using reflection. Use this
+ * annotation to specify the property name to associate with the data stored by a setter method or retrieved by a getter
+ * method in a class implementing {@link TableEntity} that uses reflection-based serialization and deserialization. Note
+ * that the names "PartitionKey", "RowKey", "Timestamp", and "Etag" are reserved and will be ignored if set with the
+ * @StoreAs
annotation.
+ * @StoreAs(name = "EntityPropertyName")
+ *
public String getObjectPropertyName() { ... }@StoreAs(name = "EntityPropertyName")
+ *
public void setObjectPropertyName(String name) { ... }ArrayList
.
+ *
+ * @param index
+ * The index in the batch operation ArrayList
to add the table operation at.
+ * @param element
+ * The {@link TableOperation} to add to the batch operation.
+ */
+ @Override
+ public void add(final int index, final TableOperation element) {
+ Utility.assertNotNull("element", element);
+
+ this.checkSingleQueryPerBatch(element);
+
+ if (element.getOperationType() == TableOperationType.RETRIEVE) {
+ this.lockToPartitionKey(((QueryTableOperation) element).getPartitionKey());
+ }
+ else {
+ this.lockToPartitionKey(element.getEntity().getPartitionKey());
+ }
+ super.add(index, element);
+ }
+
+ /**
+ * Adds the table operation to the batch operation ArrayList
.
+ *
+ * @param element
+ * The {@link TableOperation} to add to the batch operation.
+ * @return
+ * true
if the operation was added successfully.
+ */
+ @Override
+ public boolean add(final TableOperation element) {
+ Utility.assertNotNull("element", element);
+ this.checkSingleQueryPerBatch(element);
+ if (element.getEntity() == null) {
+ // Query operation
+ this.lockToPartitionKey(((QueryTableOperation) element).getPartitionKey());
+ }
+ else {
+ this.lockToPartitionKey(element.getEntity().getPartitionKey());
+ }
+
+ return super.add(element);
+ }
+
+ /**
+ * Adds the collection of table operations to the batch operation ArrayList
starting at the specified
+ * index.
+ *
+ * @param index
+ * The index in the batch operation ArrayList
to add the table operation at.
+ * @param c
+ * The collection of {@link TableOperation} objects to add to the batch operation.
+ * @return
+ * true
if the operations were added successfully.
+ */
+ @Override
+ public boolean addAll(final int index, final java.util.Collection extends TableOperation> c) {
+ for (final TableOperation operation : c) {
+ Utility.assertNotNull("operation", operation);
+ this.checkSingleQueryPerBatch(operation);
+
+ if (operation.getEntity() == null) {
+ // Query operation
+ this.lockToPartitionKey(((QueryTableOperation) operation).getPartitionKey());
+ }
+ else {
+ this.lockToPartitionKey(operation.getEntity().getPartitionKey());
+ }
+ }
+
+ return super.addAll(index, c);
+ }
+
+ /**
+ * Adds the collection of table operations to the batch operation ArrayList
.
+ *
+ * @param c
+ * The collection of {@link TableOperation} objects to add to the batch operation.
+ * @return
+ * true
if the operations were added successfully.
+ */
+ @Override
+ public boolean addAll(final java.util.Collection extends TableOperation> c) {
+ for (final TableOperation operation : c) {
+ Utility.assertNotNull("operation", operation);
+ this.checkSingleQueryPerBatch(operation);
+
+ if (operation.getEntity() == null) {
+ // Query operation
+ this.lockToPartitionKey(((QueryTableOperation) operation).getPartitionKey());
+ }
+ else {
+ this.lockToPartitionKey(operation.getEntity().getPartitionKey());
+ }
+ }
+
+ return super.addAll(c);
+ }
+
+ /**
+ * Clears all table operations from the batch operation.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ checkResetEntityLocks();
+ }
+
+ /**
+ * Adds a table operation to delete the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to delete.
+ */
+ public void delete(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.delete(entity));
+ }
+
+ /**
+ * Adds a table operation to insert the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to insert.
+ */
+ public void insert(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.insert(entity));
+ }
+
+ /**
+ * Adds a table operation to insert or merge the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to insert if not found or to merge if it exists.
+ */
+ public void insertOrMerge(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.insertOrMerge(entity));
+ }
+
+ /**
+ * Adds a table operation to insert or replace the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to insert if not found or to replace if it exists.
+ */
+ public void insertOrReplace(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.insertOrReplace(entity));
+ }
+
+ /**
+ * Adds a table operation to merge the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to merge.
+ */
+ public void merge(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.merge(entity));
+ }
+
+ /**
+ * Adds a table operation to retrieve an entity of the specified class type with the specified PartitionKey and
+ * RowKey to the batch operation.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey of the entity to retrieve.
+ * @param rowKey
+ * A String
containing the RowKey of the entity to retrieve.
+ * @param clazzType
+ * The class of the {@link TableEntity} type for the entity to retrieve.
+ */
+ public void retrieve(final String partitionKey, final String rowKey, final Class extends TableEntity> clazzType) {
+ this.lockToPartitionKey(partitionKey);
+ this.add(TableOperation.retrieve(partitionKey, rowKey, clazzType));
+ }
+
+ /**
+ * Adds a table operation to retrieve an entity of the specified class type with the specified PartitionKey and
+ * RowKey to the batch operation.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey of the entity to retrieve.
+ * @param rowKey
+ * A String
containing the RowKey of the entity to retrieve.
+ * @param resolver
+ * The {@link EntityResolver} implementation to project the entity to retrieve as a particular type in
+ * the result.
+ */
+ public void retrieve(final String partitionKey, final String rowKey, final EntityResolver> resolver) {
+ this.lockToPartitionKey(partitionKey);
+ this.add(TableOperation.retrieve(partitionKey, rowKey, resolver));
+ }
+
+ /**
+ * Removes the table operation at the specified index from the batch operation.
+ *
+ * @param index
+ * The index in the ArrayList
of the table operation to remove from the batch operation.
+ */
+ @Override
+ public TableOperation remove(int index) {
+ TableOperation op = super.remove(index);
+ checkResetEntityLocks();
+ return op;
+ }
+
+ /**
+ * Removes the specified Object
from the batch operation.
+ *
+ * @param o
+ * The Object
to remove from the batch operation.
+ * @return
+ * true
if the object was removed successfully.
+ */
+ @Override
+ public boolean remove(Object o) {
+ boolean ret = super.remove(o);
+ checkResetEntityLocks();
+ return ret;
+ }
+
+ /**
+ * Removes all elements of the specified collection from the batch operation.
+ *
+ * @param c
+ * The collection of elements to remove from the batch operation.
+ * @return
+ * true
if the objects in the collection were removed successfully.
+ */
+ @Override
+ public boolean removeAll(java.util.Collection> c) {
+ boolean ret = super.removeAll(c);
+ checkResetEntityLocks();
+ return ret;
+ }
+
+ /**
+ * Adds a table operation to replace the specified entity to the batch operation.
+ *
+ * @param entity
+ * The {@link TableEntity} to replace.
+ */
+ public void replace(final TableEntity entity) {
+ this.lockToPartitionKey(entity.getPartitionKey());
+ this.add(TableOperation.replace(entity));
+ }
+
+ /**
+ * Reserved for internal use. Clears internal fields when the batch operation is empty.
+ */
+ private void checkResetEntityLocks() {
+ if (this.size() == 0) {
+ this.partitionKey = null;
+ this.hasQuery = false;
+ }
+ }
+
+ /**
+ * Reserved for internal use. Verifies that the batch operation either contains no retrieve operations, or contains
+ * only a single retrieve operation.
+ *
+ * @param op
+ * The {@link TableOperation} to be added if the verification succeeds.
+ */
+ private void checkSingleQueryPerBatch(final TableOperation op) {
+ // if this has a query then no other operations can be added.
+ if (this.hasQuery) {
+ throw new IllegalArgumentException(
+ "A batch transaction with a retrieve operation cannot contain any other operations.");
+ }
+
+ if (op.opType == TableOperationType.RETRIEVE) {
+ if (this.size() > 0) {
+ throw new IllegalArgumentException(
+ "A batch transaction with a retrieve operation cannot contain any other operations.");
+ }
+ else {
+ this.hasQuery = true;
+ }
+ }
+ }
+
+ /**
+ * Reserved for internal use. Verifies that the specified PartitionKey value matches the value in the batch
+ * operation.
+ *
+ * @param partitionKey
+ * The String
containing the PartitionKey value to check.
+ */
+ private void lockToPartitionKey(final String partitionKey) {
+ if (this.partitionKey == null) {
+ this.partitionKey = partitionKey;
+ }
+ else {
+ if (partitionKey.length() != partitionKey.length() || !this.partitionKey.equals(partitionKey)) {
+ throw new IllegalArgumentException("All entities in a given batch must have the same partition key.");
+ }
+ }
+ }
+
+ /**
+ * Reserved for internal use. Executes this batch operation on the specified table, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * An ArrayList
of {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ protected ArrayListArrayList
.
+ *
+ * @param fromIndex
+ * The inclusive lower bound of the range of {@link TableOperation} objects to remove from the batch
+ * operation ArrayList
.
+ * @param toIndex
+ * The exclusive upper bound of the range of {@link TableOperation} objects to remove from the batch
+ * operation ArrayList
.
+ */
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ super.removeRange(fromIndex, toIndex);
+ checkResetEntityLocks();
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableConstants.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableConstants.java
new file mode 100644
index 0000000000000..48d3f3d56ea09
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableConstants.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+/**
+ * Holds the constants used for the Table Service.
+ */
+public final class TableConstants {
+ /**
+ * The constants used in HTML header fields for Table service requests.
+ */
+ public static class HeaderConstants {
+ /**
+ * The ETag header field label.
+ */
+ public static final String ETAG = "ETag";
+
+ /**
+ * The Accept header value to send.
+ */
+ public static final String ACCEPT_TYPE = "application/atom+xml,application/xml";
+
+ /**
+ * The Content-Type header value to send for single operations.
+ */
+ public static final String ATOMPUB_TYPE = "application/atom+xml";
+
+ /**
+ * The Content-Type header value to send for batch operations.
+ */
+ public static final String MULTIPART_MIXED_FORMAT = "multipart/mixed; boundary=%s";
+
+ /**
+ * The DataServiceVersion header field label.
+ */
+ public static final String DATA_SERVICE_VERSION = "DataServiceVersion";
+
+ /**
+ * The DataServiceVersion header value to send.
+ */
+ public static final String DATA_SERVICE_VERSION_VALUE = "1.0;NetFx";
+
+ /**
+ * The MaxDataServiceVersion header field label.
+ */
+ public static final String MAX_DATA_SERVICE_VERSION = "MaxDataServiceVersion";
+
+ /**
+ * The MaxDataServiceVersion header value to send.
+ */
+ public static final String MAX_DATA_SERVICE_VERSION_VALUE = "2.0;NetFx";
+ }
+
+ /**
+ * Default client side timeout, in milliseconds, for table clients.
+ */
+ public static final int TABLE_DEFAULT_TIMEOUT_IN_MS = 60 * 1000;
+
+ /**
+ * Stores the header prefix for continuation information.
+ */
+ public static final String TABLE_SERVICE_PREFIX_FOR_TABLE_CONTINUATION = "x-ms-continuation-";
+
+ /**
+ * Stores the header suffix for the next partition key.
+ */
+ public static final String TABLE_SERVICE_NEXT_PARTITION_KEY = "NextPartitionKey";
+
+ /**
+ * Stores the header suffix for the next row key.
+ */
+ public static final String TABLE_SERVICE_NEXT_ROW_KEY = "NextRowKey";
+
+ /**
+ * Stores the header suffix for the next marker.
+ */
+ public static final String TABLE_SERVICE_NEXT_MARKER = "NextMarker";
+
+ /**
+ * Stores the table suffix for the next table name.
+ */
+ public static final String TABLE_SERVICE_NEXT_TABLE_NAME = "NextTableName";
+
+ /**
+ * The name of the partition key property.
+ */
+ public static final String PARTITION_KEY = "PartitionKey";
+
+ /**
+ * The name of the row key property.
+ */
+ public static final String ROW_KEY = "RowKey";
+
+ /**
+ * The name of the Timestamp property.
+ */
+ public static final String TIMESTAMP = "Timestamp";
+
+ /**
+ * The name of the special table used to store tables.
+ */
+ public static final String TABLES_SERVICE_TABLES_NAME = "Tables";
+
+ /**
+ * The name of the property that stores the table name.
+ */
+ public static final String TABLE_NAME = "TableName";
+
+ /**
+ * The query filter clause name.
+ */
+ public static final String FILTER = "$filter";
+
+ /**
+ * The query top clause name.
+ */
+ public static final String TOP = "$top";
+
+ /**
+ * The query select clause name.
+ */
+ public static final String SELECT = "$select";
+
+ /**
+ * Private Default Constructor.
+ */
+ private TableConstants() {
+ // No op
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableEntity.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableEntity.java
new file mode 100644
index 0000000000000..85dc20a8c37bf
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableEntity.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.Date;
+import java.util.HashMap;
+
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+
+/**
+ * An interface required for table entity types. The {@link TableEntity} interface declares getter and setter methods
+ * for the common entity properties, and writeEntity
and readEntity
methods for serialization
+ * and deserialization of all entity properties using a property map. Create classes implementing {@link TableEntity} to
+ * customize property storage, retrieval, serialization and deserialization, and to provide additional custom logic for
+ * a table entity.
+ * writeEntity
and readEntity
methods.
+ * {@link TableServiceEntity}-derived classes with methods that follow a convention for types and naming are serialized
+ * and deserialized automatically.
+ * TableServiceEntity.readEntityWithReflection
in readEntity
and
+ * TableServiceEntity.writeEntityWithReflection
in writeEntity
. The class must provide methods
+ * that follow the type and naming convention to be serialized and deserialized automatically. When both a getter method
+ * and setter method are found for a given property name and data type, then the appropriate method is invoked
+ * automatically to serialize or deserialize the data. The reflection code looks for getter and setter methods in pairs
+ * of the form
+ * public type getPropertyName() { ... }
+ * public void setPropertyName(type parameter) { ... }
+ * name
+ * attribute to specify a property name for reflection on getter and setter methods that do not follow the property name
+ * convention. Method names and the name
attribute of {@link StoreAs} annotations are case sensitive for
+ * matching property names with reflection. Use the {@link Ignore} annotation to prevent methods from being used by
+ * reflection for automatic serialization and deserialization. Note that the names "PartitionKey", "RowKey",
+ * "Timestamp", and "Etag" are reserved and will be ignored if set with the {@link StoreAs} annotation in a subclass
+ * that uses the reflection methods.
+ * String
containing the Etag for the entity.
+ */
+ public String getEtag();
+
+ /**
+ * Gets the PartitionKey value for the entity.
+ *
+ * @return
+ * A String
containing the PartitionKey value for the entity.
+ */
+ public String getPartitionKey();
+
+ /**
+ * Gets the RowKey value for the entity.
+ *
+ * @return
+ * A String
containing the RowKey value for the entity.
+ */
+ public String getRowKey();
+
+ /**
+ * Gets the Timestamp for the entity.
+ *
+ * @return
+ * A Date
containing the Timestamp value for the entity.
+ */
+ public Date getTimestamp();
+
+ /**
+ * Populates an instance of the object implementing {@link TableEntity} using the specified properties parameter,
+ * containing a map of String
property names to {@link EntityProperty} data typed values.
+ *
+ * @param properties
+ * The java.util.HashMap
of String
to {@link EntityProperty} data typed values
+ * to use to populate the table entity instance.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @throws StorageException
+ * if an error occurs during the operation.
+ */
+ public void readEntity(HashMapString
containing the Etag to set for the entity.
+ */
+ public void setEtag(String etag);
+
+ /**
+ * Sets the PartitionKey value for the entity.
+ *
+ * @param partitionKey
+ * The String
containing the PartitionKey value to set for the entity.
+ */
+ public void setPartitionKey(String partitionKey);
+
+ /**
+ * Sets the RowKey value for the entity.
+ *
+ * @param rowKey
+ * The String
containing the RowKey value to set for the entity.
+ */
+ public void setRowKey(String rowKey);
+
+ /**
+ * Sets the Timestamp value for the entity.
+ *
+ * @param timeStamp
+ * The Date
containing the Timestamp value to set for the entity.
+ */
+ public void setTimestamp(Date timeStamp);
+
+ /**
+ * Returns a map of String
property names to {@link EntityProperty} data typed values
+ * that represents the serialized content of the table entity instance.
+ *
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @return
+ * The java.util.HashMap
of String
property names to {@link EntityProperty} data
+ * typed values representing the properties of the table entity.
+ *
+ * @throws StorageException
+ * if an error occurs during the operation.
+ */
+ public HashMapexecute
method on a {@link CloudTableClient} instance. A {@link TableOperation} may be executed directly
+ * or as part of a {@link TableBatchOperation}. If a {@link TableOperation} returns an entity result, it is stored in
+ * the corresponding {@link TableResult} returned by the execute
method.
+ *
+ */
+public class TableOperation {
+ /**
+ * A static factory method returning a {@link TableOperation} instance to delete the specified entity from Windows
+ * Azure storage. To execute this {@link TableOperation} on a given table, call the
+ * {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with the
+ * table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance to insert the table entity.
+ */
+ public static TableOperation delete(final TableEntity entity) {
+ Utility.assertNotNull("Entity", entity);
+ Utility.assertNotNullOrEmpty("Entity Etag", entity.getEtag());
+ return new TableOperation(entity, TableOperationType.DELETE);
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to insert the specified entity into Windows
+ * Azure storage. To execute this {@link TableOperation} on a given table, call the
+ * {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with the
+ * table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance to insert the table entity.
+ */
+ public static TableOperation insert(final TableEntity entity) {
+ Utility.assertNotNull("Entity", entity);
+ return new TableOperation(entity, TableOperationType.INSERT);
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to merge the specified entity into Windows
+ * Azure storage, or insert it if it does not exist. To execute this {@link TableOperation} on a given table, call
+ * the {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with
+ * the table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance for inserting or merging the table entity.
+ */
+ public static TableOperation insertOrMerge(final TableEntity entity) {
+ Utility.assertNotNull("Entity", entity);
+ return new TableOperation(entity, TableOperationType.INSERT_OR_MERGE);
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to replace the specified entity in Windows
+ * Azure storage, or insert it if it does not exist. To execute this {@link TableOperation} on a given table, call
+ * the {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with
+ * the table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance for inserting or replacing the table entity.
+ */
+ public static TableOperation insertOrReplace(final TableEntity entity) {
+ Utility.assertNotNull("Entity", entity);
+ return new TableOperation(entity, TableOperationType.INSERT_OR_REPLACE);
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to merge the specified table entity into
+ * Windows Azure storage. To execute this {@link TableOperation} on a given table, call the
+ * {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with the
+ * table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance for merging the table entity.
+ */
+ public static TableOperation merge(final TableEntity entity) {
+ Utility.assertNotNull("Entity", entity);
+ Utility.assertNotNullOrEmpty("Entity Etag", entity.getEtag());
+ return new TableOperation(entity, TableOperationType.MERGE);
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to retrieve the specified table entity and
+ * return it as the specified type. To execute this {@link TableOperation} on a given table, call the
+ * {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with the
+ * table name and the {@link TableOperation} as arguments.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity to retrieve.
+ * @param rowKey
+ * A String
containing the RowKey value for the entity to retrieve.
+ * @param clazzType
+ * The class type of the table entity object to retrieve.
+ * @return
+ * A new {@link TableOperation} instance for retrieving the table entity.
+ */
+ public static TableOperation retrieve(final String partitionKey, final String rowKey,
+ final Class extends TableEntity> clazzType) {
+ final QueryTableOperation retOp = new QueryTableOperation(partitionKey, rowKey);
+ retOp.setClazzType(clazzType);
+ return retOp;
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to retrieve the specified table entity and
+ * return a projection of it using the specified resolver. To execute this {@link TableOperation} on a given table,
+ * call the {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance
+ * with the table name and the {@link TableOperation} as arguments.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity to retrieve.
+ * @param rowKey
+ * A String
containing the RowKey value for the entity to retrieve.
+ * @param resolver
+ * The implementation of {@link EntityResolver} to use to project the result entity as type T.
+ * @return
+ * A new {@link TableOperation} instance for retrieving the table entity.
+ */
+ public static TableOperation retrieve(final String partitionKey, final String rowKey,
+ final EntityResolver> resolver) {
+ final QueryTableOperation retOp = new QueryTableOperation(partitionKey, rowKey);
+ retOp.setResolver(resolver);
+ return retOp;
+ }
+
+ /**
+ * A static factory method returning a {@link TableOperation} instance to replace the specified table entity. To
+ * execute this {@link TableOperation} on a given table, call the
+ * {@link CloudTableClient#execute(String, TableOperation)} method on a {@link CloudTableClient} instance with the
+ * table name and the {@link TableOperation} as arguments.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @return
+ * A new {@link TableOperation} instance for replacing the table entity.
+ */
+ public static TableOperation replace(final TableEntity entity) {
+ Utility.assertNotNullOrEmpty("Entity Etag", entity.getEtag());
+ return new TableOperation(entity, TableOperationType.REPLACE);
+ }
+
+ /**
+ * The table entity instance associated with the operation.
+ */
+ TableEntity entity;
+
+ /**
+ * The {@link TableOperationType} enumeration value for the operation type.
+ */
+ TableOperationType opType = null;
+
+ /**
+ * Nullary Default Constructor.
+ */
+ protected TableOperation() {
+ // empty ctor
+ }
+
+ /**
+ * Reserved for internal use. Constructs a {@link TableOperation} with the specified table entity and operation
+ * type.
+ *
+ * @param entity
+ * The object instance implementing {@link TableEntity} to associate with the operation.
+ * @param opType
+ * The {@link TableOperationType} enumeration value for the operation type.
+ */
+ protected TableOperation(final TableEntity entity, final TableOperationType opType) {
+ this.entity = entity;
+ this.opType = opType;
+ }
+
+ /**
+ * Reserved for internal use. Performs a delete operation on the specified table, using the specified
+ * {@link TableRequestOptions} and {@link OperationContext}.
+ * String
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ private TableResult performDelete(final CloudTableClient client, final String tableName,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ final boolean isTableEntry = TableConstants.TABLES_SERVICE_TABLES_NAME.equals(tableName);
+ final String tableIdentity = isTableEntry ? this.getEntity().writeEntity(opContext)
+ .get(TableConstants.TABLE_NAME).getValueAsString() : null;
+
+ if (!isTableEntry) {
+ Utility.assertNotNullOrEmpty("Delete requires a valid ETag", this.getEntity().getEtag());
+ Utility.assertNotNullOrEmpty("Delete requires a valid PartitionKey", this.getEntity().getPartitionKey());
+ Utility.assertNotNullOrEmpty("Delete requires a valid RowKey", this.getEntity().getRowKey());
+ }
+
+ final StorageOperationString
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ private TableResult performInsert(final CloudTableClient client, final String tableName,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ final boolean isTableEntry = TableConstants.TABLES_SERVICE_TABLES_NAME.equals(tableName);
+ final String tableIdentity = isTableEntry ? this.getEntity().writeEntity(opContext)
+ .get(TableConstants.TABLE_NAME).getValueAsString() : null;
+
+ // Upserts need row key and partition key
+ if (!isTableEntry && this.opType != TableOperationType.INSERT) {
+ Utility.assertNotNullOrEmpty("Upserts require a valid PartitionKey", this.getEntity().getPartitionKey());
+ Utility.assertNotNullOrEmpty("Upserts require a valid RowKey", this.getEntity().getRowKey());
+ }
+
+ final StorageOperationString
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ private TableResult performMerge(final CloudTableClient client, final String tableName,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ Utility.assertNotNullOrEmpty("Merge requires a valid ETag", this.getEntity().getEtag());
+ Utility.assertNotNullOrEmpty("Merge requires a valid PartitionKey", this.getEntity().getPartitionKey());
+ Utility.assertNotNullOrEmpty("Merge requires a valid RowKey", this.getEntity().getRowKey());
+
+ final StorageOperationString
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ private TableResult performUpdate(final CloudTableClient client, final String tableName,
+ final TableRequestOptions options, final OperationContext opContext) throws StorageException {
+ Utility.assertNotNullOrEmpty("Update requires a valid ETag", this.getEntity().getEtag());
+ Utility.assertNotNullOrEmpty("Update requires a valid PartitionKey", this.getEntity().getPartitionKey());
+ Utility.assertNotNullOrEmpty("Update requires a valid RowKey", this.getEntity().getRowKey());
+ final StorageOperationString
containing the name of the table.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * A {@link TableResult} containing the results of executing the operation.
+ *
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ protected TableResult execute(final CloudTableClient client, final String tableName, TableRequestOptions options,
+ OperationContext opContext) throws StorageException {
+ if (opContext == null) {
+ opContext = new OperationContext();
+ }
+
+ if (options == null) {
+ options = new TableRequestOptions();
+ }
+
+ opContext.initialize();
+ options.applyDefaults(client);
+ Utility.assertNotNullOrEmpty("TableName", tableName);
+
+ if (this.getOperationType() == TableOperationType.INSERT
+ || this.getOperationType() == TableOperationType.INSERT_OR_MERGE
+ || this.getOperationType() == TableOperationType.INSERT_OR_REPLACE) {
+ return this.performInsert(client, tableName, options, opContext);
+ }
+ else if (this.getOperationType() == TableOperationType.DELETE) {
+ return this.performDelete(client, tableName, options, opContext);
+ }
+ else if (this.getOperationType() == TableOperationType.MERGE) {
+ return this.performMerge(client, tableName, options, opContext);
+ }
+ else if (this.getOperationType() == TableOperationType.REPLACE) {
+ return this.performUpdate(client, tableName, options, opContext);
+ }
+ else if (this.getOperationType() == TableOperationType.RETRIEVE) {
+ return ((QueryTableOperation) this).performRetrieve(client, tableName, options, opContext);
+ }
+ else {
+ throw new IllegalArgumentException("Unknown table operation");
+ }
+ }
+
+ /**
+ * Reserved for internal use. Generates the request identity, consisting of the specified entry name, or the
+ * PartitionKey and RowKey pair from the operation, to identify the operation target.
+ *
+ * @param isSingleIndexEntry
+ * Pass true
to use the specified entryName
parameter, or false
to
+ * use PartitionKey and RowKey values from the operation as the request identity.
+ * @param entryName
+ * The entry name to use as the request identity if the isSingleIndexEntry
parameter is
+ * true
.
+ * @return
+ * A String
containing the formatted request identity string.
+ */
+ protected String generateRequestIdentity(boolean isSingleIndexEntry, final String entryName) {
+ if (isSingleIndexEntry) {
+ return String.format("'%s'", entryName);
+ }
+
+ if (this.opType == TableOperationType.INSERT) {
+ return Constants.EMPTY_STRING;
+ }
+ else {
+ String pk = null;
+ String rk = null;
+
+ if (this.opType == TableOperationType.RETRIEVE) {
+ final QueryTableOperation qOp = (QueryTableOperation) this;
+ pk = qOp.getPartitionKey();
+ rk = qOp.getRowKey();
+ }
+ else {
+ pk = this.getEntity().getPartitionKey();
+ rk = this.getEntity().getRowKey();
+ }
+
+ return String.format("%s='%s',%s='%s'", TableConstants.PARTITION_KEY, pk, TableConstants.ROW_KEY, rk);
+ }
+ }
+
+ /**
+ * Reserved for internal use. Generates the request identity string for the specified table. The request identity
+ * string combines the table name with the PartitionKey and RowKey from the operation to identify specific table
+ * entities.
+ *
+ * @param tableName
+ * A String
containing the name of the table.
+ * @return
+ * A String
containing the formatted request identity string for the specified table.
+ */
+ protected String generateRequestIdentityWithTable(final String tableName) {
+ return String.format("/%s(%s)", tableName, generateRequestIdentity(false, null));
+ }
+
+ /**
+ * Reserved for internal use. Gets the table entity associated with this operation.
+ *
+ * @return
+ * The {@link TableEntity} instance associated with this operation.
+ */
+ protected synchronized final TableEntity getEntity() {
+ return this.entity;
+ }
+
+ /**
+ * Reserved for internal use. Gets the operation type for this operation.
+ *
+ * @return the opType
+ * The {@link TableOperationType} instance associated with this operation.
+ */
+ protected synchronized final TableOperationType getOperationType() {
+ return this.opType;
+ }
+
+ /**
+ * Reserved for internal use. Parses the table operation response into a {@link TableResult} to return.
+ *
+ * @param xmlr
+ * An XMLStreamReader
containing the response to an insert operation.
+ * @param httpStatusCode
+ * The HTTP status code returned from the operation request.
+ * @param etagFromHeader
+ * The String
containing the Etag returned with the operation response.
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation.
+ *
+ * @return
+ * The {@link TableResult} representing the result of the operation.
+ *
+ * @throws XMLStreamException
+ * if an error occurs accessing the XMLStreamReader
.
+ * @throws ParseException
+ * if an error occurs in parsing the response.
+ * @throws InstantiationException
+ * if an error occurs in object construction.
+ * @throws IllegalAccessException
+ * if an error occurs in reflection on an object type.
+ * @throws StorageException
+ * if an error occurs in the storage operation.
+ */
+ protected TableResult parseResponse(final XMLStreamReader xmlr, final int httpStatusCode,
+ final String etagFromHeader, final OperationContext opContext) throws XMLStreamException, ParseException,
+ InstantiationException, IllegalAccessException, StorageException {
+ TableResult resObj = null;
+ if (this.opType == TableOperationType.INSERT) {
+ // Sending null for class type and resolver will ignore parsing the return payload.
+ resObj = AtomPubParser.parseSingleOpResponse(xmlr, httpStatusCode, null, null, opContext);
+ resObj.updateResultObject(this.getEntity());
+ }
+ else {
+ resObj = new TableResult(httpStatusCode);
+ resObj.setResult(this.getEntity());
+
+ if (this.opType != TableOperationType.DELETE) {
+ this.getEntity().setEtag(etagFromHeader);
+ }
+ }
+
+ return resObj;
+ }
+
+ /**
+ * Reserved for internal use. Sets the {@link TableEntity} instance for the table operation.
+ *
+ * @param entity
+ * The {@link TableEntity} instance to set.
+ */
+ protected synchronized final void setEntity(final TableEntity entity) {
+ this.entity = entity;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperationType.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperationType.java
new file mode 100644
index 0000000000000..a1ceccabd3320
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableOperationType.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+/**
+ * Reserved for internal use. An enumeration type which represents the type of operation a {@link TableOperation}
+ * represents.
+ */
+enum TableOperationType {
+ INSERT, DELETE, REPLACE, RETRIEVE, MERGE, INSERT_OR_REPLACE, INSERT_OR_MERGE;
+
+ /**
+ * Gets the {@link TableUpdateType} associated the operation type, if applicable. Applies to
+ * {@link #INSERT_OR_MERGE} and {@link #INSERT_OR_REPLACE} values.
+ *
+ * @return
+ * The applicable {@link TableUpdateType}, or null
.
+ */
+ public TableUpdateType getUpdateType() {
+ if (this == INSERT_OR_MERGE) {
+ return TableUpdateType.MERGE;
+ }
+ else if (this == INSERT_OR_REPLACE) {
+ return TableUpdateType.REPLACE;
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableQuery.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableQuery.java
new file mode 100644
index 0000000000000..a54279ae0ce17
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableQuery.java
@@ -0,0 +1,773 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.Date;
+import java.util.Formatter;
+import java.util.UUID;
+
+import com.microsoft.windowsazure.services.core.storage.Constants;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+import com.microsoft.windowsazure.services.core.storage.utils.UriQueryBuilder;
+import com.microsoft.windowsazure.services.core.storage.utils.Utility;
+
+/**
+ * A class which represents a query against a specified table. A {@link TableQuery} instance aggregates the query
+ * parameters to use when the query is executed. One of the execute
or executeSegmented
+ * methods of {@link CloudTableClient} must be called to execute the query. The parameters are encoded and passed to the
+ * server when the table query is executed.
+ * TableQuery
instance that executes on the named table with entities of the specified {@link TableEntity}
+ * implementing type. Use the {@link #where} method to specify a filter expression for the entities returned. Use the
+ * {@link #select} method to specify the table entity properties to return. Use the {@link #take} method to limit the
+ * number of entities returned by the query. Note that nothing prevents calling these methods more than once on a
+ * TableQuery
, so the values saved in the TableQuery
will be the last encountered in order of
+ * execution.
+ * TableQuery<TableServiceEntity> myQuery = TableQuery.from("Products", DynamicTableEntity.class)
+ *
+ *     .where("(PartitionKey eq 'ProductsMNO') and (RowKey ge 'Napkin')")
+ *     .take(25)
+ *     .select(new String[] {"InventoryCount"});String
types for comparison purposes.
+ * .class
as the class type.
+ *
+ * @param .class
as the class type parameter if no more specialized type is required.
+ *
+ * @param tablename
+ * A String
containing the name of the source table to query.
+ * @param clazzType
+ * The java.lang.Class
of the class T
implementing the {@link TableEntity}
+ * interface that
+ * represents the table entity type for the query.
+ *
+ * @return
+ * The {@link TableQuery} instance with the source table name and entity type specialization set.
+ */
+ public static boolean
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as a
+ * boolean, as in the following example:
+ * String condition = generateFilterCondition("BooleanProperty", QueryComparisons.EQUAL, false);
+ * condition
to the following value:
+ * BooleanProperty eq false
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A boolean
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final boolean value) {
+ return generateFilterCondition(propertyName, operation, value ? Constants.TRUE : Constants.FALSE,
+ EdmType.BOOLEAN);
+ }
+
+ /**
+ * Generates a property filter condition string for a byte[]
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as a
+ * binary value, as in the following example:
+ * String condition = generateFilterCondition("ByteArray", QueryComparisons.EQUAL, new byte[] {0x01, 0x0f});
+ * condition
to the following value:
+ * ByteArray eq X'010f'
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A byte[]
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final byte[] value) {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb);
+ for (byte b : value) {
+ formatter.format("%02x", b);
+ }
+
+ return generateFilterCondition(propertyName, operation, sb.toString(), EdmType.BINARY);
+ }
+
+ /**
+ * Generates a property filter condition string for a Byte[]
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as a
+ * binary value, as in the following example:
+ * String condition = generateFilterCondition("ByteArray", QueryComparisons.EQUAL, new Byte[] {0x01, 0xfe});
+ * condition
to the following value:
+ * ByteArray eq X'01fe'
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A Byte[]
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final Byte[] value) {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb);
+ for (byte b : value) {
+ formatter.format("%02x", b);
+ }
+
+ return generateFilterCondition(propertyName, operation, sb.toString(), EdmType.BINARY);
+ }
+
+ /**
+ * Generates a property filter condition string for a Date
value. Creates a formatted string to use in
+ * a filter expression that uses the specified operation to compare the property with the value, formatted as a
+ * datetime value, as in the following example:
+ * String condition = generateFilterCondition("FutureDate", QueryComparisons.GREATER_THAN, new Date());
+ * condition
to something like the following value:
+ * FutureDate gt datetime'2013-01-31T09:00:00'
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A Date
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final Date value) {
+ return generateFilterCondition(propertyName, operation,
+ Utility.getTimeByZoneAndFormat(value, Utility.UTC_ZONE, Utility.ISO8061_LONG_PATTERN),
+ EdmType.DATE_TIME);
+ }
+
+ /**
+ * Generates a property filter condition string for a double
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as
+ * a double value, as in the following example:
+ * String condition = generateFilterCondition("Circumference", QueryComparisons.EQUAL, 2 * 3.141592);
+ * condition
to the following value:
+ * Circumference eq 6.283184
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A double
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final double value) {
+ return generateFilterCondition(propertyName, operation, Double.toString(value), EdmType.DOUBLE);
+ }
+
+ /**
+ * Generates a property filter condition string for an int
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as
+ * a numeric value, as in the following example:
+ * String condition = generateFilterCondition("Population", QueryComparisons.GREATER_THAN, 1000);
+ * condition
to the following value:
+ * Population gt 1000
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * An int
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final int value) {
+ return generateFilterCondition(propertyName, operation, Integer.toString(value), EdmType.INT32);
+ }
+
+ /**
+ * Generates a property filter condition string for a long
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as
+ * a numeric value, as in the following example:
+ * String condition = generateFilterCondition("StellarMass", QueryComparisons.GREATER_THAN, 7000000000L);
+ * condition
to the following value:
+ * StellarMass gt 7000000000
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A long
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final long value) {
+ return generateFilterCondition(propertyName, operation, Long.toString(value), EdmType.INT64);
+ }
+
+ /**
+ * Generates a property filter condition string for a String
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as
+ * a string value, as in the following example:
+ * String condition = generateFilterCondition("Platform", QueryComparisons.EQUAL, "Windows Azure");
+ * condition
to the following value:
+ * Platform eq 'Windows Azure'
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A String
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final String value) {
+ return generateFilterCondition(propertyName, operation, value, EdmType.STRING);
+ }
+
+ /**
+ * Generates a property filter condition string. Creates a formatted string to use in a filter expression that uses
+ * the specified operation to compare the property with the value, formatted as the specified {@link EdmType}.
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A String
containing the value to compare with the property.
+ * @param edmType
+ * The {@link EdmType} to format the value as.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, String value, EdmType edmType) {
+ String valueOperand = null;
+
+ if (edmType == EdmType.BOOLEAN || edmType == EdmType.DOUBLE || edmType == EdmType.INT32
+ || edmType == EdmType.INT64) {
+ valueOperand = value;
+ }
+ else if (edmType == EdmType.DATE_TIME) {
+ valueOperand = String.format("datetime'%s'", value);
+ }
+ else if (edmType == EdmType.GUID) {
+ valueOperand = String.format("guid'%s'", value);
+ }
+ else if (edmType == EdmType.BINARY) {
+ valueOperand = String.format("X'%s'", value);
+ }
+ else {
+ valueOperand = String.format("'%s'", value);
+ }
+
+ return String.format("%s %s %s", propertyName, operation, valueOperand);
+ }
+
+ /**
+ * Generates a property filter condition string for a UUID
value. Creates a formatted string to use
+ * in a filter expression that uses the specified operation to compare the property with the value, formatted as
+ * a UUID value, as in the following example:
+ * String condition = generateFilterCondition("Identity", QueryComparisons.EQUAL, UUID.fromString(
+ * "c9da6455-213d-42c9-9a79-3e9149a57833"));
+ * condition
to the following value:
+ * Identity eq guid'c9da6455-213d-42c9-9a79-3e9149a57833'
+ *
+ * @param propertyName
+ * A String
containing the name of the property to compare.
+ * @param operation
+ * A String
containing the comparison operator to use.
+ * @param value
+ * A UUID
containing the value to compare with the property.
+ * @return
+ * A String
containing the formatted filter condition.
+ */
+ public static String generateFilterCondition(String propertyName, String operation, final UUID value) {
+ return generateFilterCondition(propertyName, operation, value.toString(), EdmType.GUID);
+ }
+
+ /**
+ * Creates a filter condition using the specified logical operator on two filter conditions.
+ *
+ * @param filterA
+ * A String
containing the first formatted filter condition.
+ * @param operator
+ * A String
containing Operators.AND
or Operators.OR
.
+ * @param filterB
+ * A String
containing the first formatted filter condition.
+ * @return
+ * A String
containing the combined filter expression.
+ */
+ public static String combineFilters(String filterA, String operator, String filterB) {
+ return String.format("(%s) %s (%s)", filterA, operator, filterB);
+ }
+
+ private Class.class
as the class type parameter if no more specialized type is
+ * required.
+ *
+ * @param tablename
+ * A String
containing the name of the source table to query.
+ * @param clazzType
+ * The java.lang.Class
of the class T
that represents the table entity type for
+ * the query. Class T
must be a type that implements {@link TableEntity} and has a nullary
+ * constructor,
+ */
+ public TableQuery(final String tableName, final Classjava.lang.Class
of the class T
that represents the table entity type for
+ * the query.
+ */
+ public ClassPartitionKey
, RowKey
, and Timestamp
are
+ * automatically requested from the table service whether specified in the {@link TableQuery} or not.
+ *
+ * @return
+ * An array of String
objects containing the property names of the table entity properties to
+ * return in the query.
+ */
+ public String[] getColumns() {
+ return this.columns;
+ }
+
+ /**
+ * Gets the class type of the table entities returned by the query.
+ *
+ * @return
+ * The java.lang.Class
of the class T
implementing the {@link TableEntity}
+ * interface that
+ * represents the table entity type for the query.
+ */
+ public ClassString
containing the filter expression used in the query.
+ */
+ public String getFilterString() {
+ return this.filterString;
+ }
+
+ /**
+ * Gets the name of the source table specified in the table query.
+ *
+ * @return
+ * A String
containing the name of the source table used in the query.
+ */
+ public String getSourceTableName() {
+ return this.sourceTableName;
+ }
+
+ /**
+ * Gets the number of entities the query returns specified in the table query. If this value is not
+ * specified in a table query, a maximum of 1,000 entries will be returned. The number of entities to return may be
+ * specified with a call to the {@link #setTakeCount} or {@link #take} methods.
+ * getTakeCount
is greater than 1,000, the query will throw a
+ * {@link StorageException} when executed.
+ *
+ * @return
+ * The maximum number of entities for the table query to return.
+ */
+ public Integer getTakeCount() {
+ return this.takeCount;
+ }
+
+ /**
+ * Defines the property names of the table entity properties to return when the table query is executed. The
+ * select
clause is optional on a table query, used to limit the table properties returned from the
+ * server. By default, a query will return all properties from the table entity.
+ * PartitionKey
, RowKey
, and Timestamp
are
+ * automatically requested from the table service whether specified in the {@link TableQuery} or not.
+ *
+ * @param columns
+ * An array of String
objects containing the property names of the table entity properties
+ * to return when the query is executed.
+ *
+ * @return
+ * A reference to the {@link TableQuery} instance with the table entity properties to return set.
+ */
+ public TableQuery.class
as the class type parameter if no more
+ * specialized type is required.
+ *
+ * @param clazzType
+ * The java.lang.Class
of the class T
that represents the table entity type for
+ * the query. Class T
must be a type that implements {@link TableEntity} and has a nullary
+ * constructor,
+ */
+ public void setClazzType(final ClassPartitionKey
, RowKey
, and Timestamp
are
+ * automatically requested from the table service whether specified in the {@link TableQuery} or not.
+ *
+ * @param columns
+ * An array of String
objects containing the property names of the table entity properties
+ * to return when the query is executed.
+ */
+ public void setColumns(final String[] columns) {
+ this.columns = columns;
+ }
+
+ /**
+ * Sets the filter expression to use in the table query. A filter expression is optional; by default a table query
+ * will return all entities in the table.
+ * String
types for comparison purposes. For
+ * example, to query all entities with a PartitionKey value of "AccessLogs" on table query myQuery
:
+ *     myQuery.setFilterString("PartitionKey eq 'AccessLogs'");
+ * String
containing the filter expression to use in the query.
+ */
+ public void setFilterString(final String filterString) {
+ Utility.assertNotNullOrEmpty("filterString", filterString);
+ this.filterString = filterString;
+ }
+
+ /**
+ * Sets the name of the source table for the table query. A table query must have a source table to be executed.
+ *
+ * @param sourceTableName
+ * A String
containing the name of the source table to use in the query.
+ */
+ public void setSourceTableName(final String sourceTableName) {
+ Utility.assertNotNullOrEmpty("tableName", sourceTableName);
+ this.sourceTableName = sourceTableName;
+ }
+
+ /**
+ * Sets the upper bound for the number of entities the query returns. If this value is not specified in a table
+ * query, by default a maximum of 1,000 entries will be returned.
+ * takeCount
parameter is greater than 1,000, the query will throw a
+ * {@link StorageException} when executed.
+ *
+ * @param takeCount
+ * The maximum number of entities for the table query to return.
+ */
+ public void setTakeCount(final Integer takeCount) {
+ if (takeCount != null && takeCount <= 0) {
+ throw new IllegalArgumentException("Take count must be positive and greater than 0.");
+ }
+
+ this.takeCount = takeCount;
+ }
+
+ /**
+ * Defines the upper bound for the number of entities the query returns. If this value is not specified in a table
+ * query, by default a maximum of 1,000 entries will be returned.
+ * take
parameter is greater than 1,000, the query will throw a
+ * {@link StorageException} when executed.
+ *
+ * @param take
+ * The maximum number of entities for the table query to return.
+ *
+ * @return
+ * A reference to the {@link TableQuery} instance with the number of entities to return set.
+ */
+ public TableQueryString
types for comparison purposes. For example, to
+ * query all entities with a PartitionKey value of "AccessLogs" on table query myQuery
:
+ *     myQuery = myQuery.where("PartitionKey eq 'AccessLogs'");
+ * String
containing the filter expression to apply to the table query.
+ * @return
+ * A reference to the {@link TableQuery} instance with the filter on entities to return set.
+ */
+ public TableQuerynull
if no continuation token values are set.
+ *
+ * @throws StorageException
+ * if an error occurs in accessing the query builder or continuation token.
+ */
+ protected static void applyContinuationToQueryBuilder(final UriQueryBuilder builder,
+ final ResultContinuation continuationToken) throws StorageException {
+ if (continuationToken != null) {
+ if (continuationToken.getNextPartitionKey() != null) {
+ builder.add(TableConstants.TABLE_SERVICE_NEXT_PARTITION_KEY, continuationToken.getNextPartitionKey());
+ }
+
+ if (continuationToken.getNextRowKey() != null) {
+ builder.add(TableConstants.TABLE_SERVICE_NEXT_ROW_KEY, continuationToken.getNextRowKey());
+ }
+
+ if (continuationToken.getNextTableName() != null) {
+ builder.add(TableConstants.TABLE_SERVICE_NEXT_TABLE_NAME, continuationToken.getNextTableName());
+ }
+ }
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection
to perform a table batch operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param batchID
+ * The String
containing the batch identifier.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param tableOptions
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. This parameter is unused.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection batch(final URI rootUri, final int timeoutInMs, final String batchID,
+ final UriQueryBuilder queryBuilder, final TableRequestOptions tableOptions, final OperationContext opContext)
+ throws IOException, URISyntaxException, StorageException {
+ final URI queryUri = PathUtility.appendPathToUri(rootUri, "$batch");
+
+ final HttpURLConnection retConnection = BaseRequest.createURLConnection(queryUri, timeoutInMs, queryBuilder,
+ opContext);
+ // Note : accept behavior, java by default sends Accept behavior
+ // as text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
+ retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT, TableConstants.HeaderConstants.ACCEPT_TYPE);
+ retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT_CHARSET, "UTF8");
+ retConnection.setRequestProperty(TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION,
+ TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION_VALUE);
+
+ retConnection.setRequestProperty(Constants.HeaderConstants.CONTENT_TYPE,
+ String.format(TableConstants.HeaderConstants.MULTIPART_MIXED_FORMAT, batchID));
+
+ retConnection.setRequestMethod("POST");
+ retConnection.setDoOutput(true);
+ return retConnection;
+ }
+
+ /**
+ * Reserved for internal use. Constructs the core HttpURLConnection
to perform an operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * The identity of the entity, to pass in the Service Managment REST operation URI as
+ * tableName(identity)
. If null
, only the tableName
+ * value will be passed.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The UriQueryBuilder
for the request.
+ * @param requestMethod
+ * The HTTP request method to set.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. This parameter is unused.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection coreCreate(final URI rootUri, final String tableName, final String eTag,
+ final String identity, final int timeoutInMs, final UriQueryBuilder queryBuilder,
+ final String requestMethod, final TableRequestOptions tableOptions, final OperationContext opContext)
+ throws IOException, URISyntaxException, StorageException {
+
+ URI queryUri = null;
+
+ // Do point query / delete etc.
+ if (!Utility.isNullOrEmpty(identity)) {
+ queryUri = PathUtility.appendPathToUri(rootUri, tableName.concat(String.format("(%s)", identity)));
+ }
+ else {
+ queryUri = PathUtility.appendPathToUri(rootUri, tableName);
+ }
+
+ final HttpURLConnection retConnection = BaseRequest.createURLConnection(queryUri, timeoutInMs, queryBuilder,
+ opContext);
+ // Note : accept behavior, java by default sends Accept behavior
+ // as text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
+ retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT, TableConstants.HeaderConstants.ACCEPT_TYPE);
+ retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT_CHARSET, "UTF-8");
+ retConnection.setRequestProperty(TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION,
+ TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION_VALUE);
+
+ retConnection.setRequestProperty(Constants.HeaderConstants.CONTENT_TYPE,
+ TableConstants.HeaderConstants.ATOMPUB_TYPE);
+
+ if (!Utility.isNullOrEmpty(eTag)) {
+ retConnection.setRequestProperty(Constants.HeaderConstants.IF_MATCH, eTag);
+ }
+
+ retConnection.setRequestMethod(requestMethod);
+ return retConnection;
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection
to perform a delete operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * The identity of the entity. The resulting request will be formatted as /tableName(identity) if not
+ * null or empty.
+ * @param eTag
+ * The etag of the entity.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection delete(final URI rootUri, final String tableName, final String identity,
+ final String eTag, final int timeoutInMs, final UriQueryBuilder queryBuilder,
+ final TableRequestOptions tableOptions, final OperationContext opContext) throws IOException,
+ URISyntaxException, StorageException {
+
+ return coreCreate(rootUri, tableName, eTag, identity, timeoutInMs, queryBuilder, "DELETE", tableOptions,
+ opContext);
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection
to perform an insert operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * The identity of the entity. The resulting request will be formatted as /tableName(identity) if not
+ * null or empty.
+ * @param eTag
+ * The etag of the entity, can be null for straight inserts.
+ * @param updateType
+ * The {@link TableUpdateType} type of update to be performed. Specify null
for straight
+ * inserts.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection insert(final URI rootUri, final String tableName, final String identity,
+ final String eTag, final TableUpdateType updateType, final int timeoutInMs,
+ final UriQueryBuilder queryBuilder, final TableRequestOptions tableOptions, final OperationContext opContext)
+ throws IOException, URISyntaxException, StorageException {
+ HttpURLConnection retConnection = null;
+
+ if (updateType == null) {
+ retConnection = coreCreate(rootUri, tableName, eTag, null/* identity */, timeoutInMs, queryBuilder,
+ "POST", tableOptions, opContext);
+ }
+ else if (updateType == TableUpdateType.MERGE) {
+ retConnection = coreCreate(rootUri, tableName, null/* ETAG */, identity, timeoutInMs, queryBuilder,
+ "POST", tableOptions, opContext);
+
+ retConnection.setRequestProperty("X-HTTP-Method", "MERGE");
+ }
+ else if (updateType == TableUpdateType.REPLACE) {
+ retConnection = coreCreate(rootUri, tableName, null/* ETAG */, identity, timeoutInMs, queryBuilder, "PUT",
+ tableOptions, opContext);
+ }
+
+ retConnection.setDoOutput(true);
+
+ return retConnection;
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection to perform a merge operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * The identity of the entity. The resulting request will be formatted as /tableName(identity) if not
+ * null or empty.
+ * @param eTag
+ * The etag of the entity.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection merge(final URI rootUri, final String tableName, final String identity,
+ final String eTag, final int timeoutInMs, final UriQueryBuilder queryBuilder,
+ final TableRequestOptions tableOptions, final OperationContext opContext) throws IOException,
+ URISyntaxException, StorageException {
+ final HttpURLConnection retConnection = coreCreate(rootUri, tableName, eTag, identity, timeoutInMs,
+ queryBuilder, "POST", tableOptions, opContext);
+ retConnection.setRequestProperty("X-HTTP-Method", "MERGE");
+ retConnection.setDoOutput(true);
+ return retConnection;
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection to perform a single entity query operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * The identity of the entity. The resulting request will be formatted as /tableName(identity) if not
+ * null or empty.
+ * @param timeoutInMs
+ * The server timeout interval in milliseconds.
+ * @param queryBuilder
+ * The {@link UriQueryBuilder} for the operation.
+ * @param options
+ * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout
+ * settings for the operation. Specify null
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection query(final URI rootUri, final String tableName, final String identity,
+ final int timeoutInMs, UriQueryBuilder queryBuilder, final ResultContinuation continuationToken,
+ final TableRequestOptions tableOptions, final OperationContext opContext) throws IOException,
+ URISyntaxException, StorageException {
+ if (queryBuilder == null) {
+ queryBuilder = new UriQueryBuilder();
+ }
+
+ applyContinuationToQueryBuilder(queryBuilder, continuationToken);
+ final HttpURLConnection retConnection = coreCreate(rootUri, tableName, null, identity, timeoutInMs,
+ queryBuilder, "GET", tableOptions, opContext);
+
+ return retConnection;
+ }
+
+ /**
+ * Reserved for internal use. Constructs an HttpURLConnection to perform an update operation.
+ *
+ * @param rootUri
+ * A java.net.URI
containing an absolute URI to the resource.
+ * @param tableName
+ * The name of the table.
+ * @param identity
+ * A String
representing the identity of the entity. The resulting request will be formatted
+ * using /tableName(identity) if identity is not >code>nullnull
to use the request options specified on the
+ * {@link CloudTableClient}.
+ * @param opContext
+ * An {@link OperationContext} object for tracking the current operation. Specify null
to
+ * safely ignore operation context.
+ *
+ * @return
+ * An HttpURLConnection
to use to perform the operation.
+ *
+ * @throws IOException
+ * if there is an error opening the connection.
+ * @throws URISyntaxException
+ * if the resource URI is invalid.
+ * @throws StorageException
+ * if a storage service error occurred during the operation.
+ */
+ protected static HttpURLConnection update(final URI rootUri, final String tableName, final String identity,
+ final String eTag, final int timeoutInMs, final UriQueryBuilder queryBuilder,
+ final TableRequestOptions tableOptions, final OperationContext opContext) throws IOException,
+ URISyntaxException, StorageException {
+ final HttpURLConnection retConnection = coreCreate(rootUri, tableName, eTag, identity, timeoutInMs,
+ queryBuilder, "PUT", tableOptions, opContext);
+
+ retConnection.setDoOutput(true);
+ return retConnection;
+ }
+
+ /**
+ * Private Default Constructor.
+ */
+ private TableRequest() {
+ // No op
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableRequestOptions.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableRequestOptions.java
new file mode 100644
index 0000000000000..1a4d210526f20
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableRequestOptions.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import com.microsoft.windowsazure.services.core.storage.RequestOptions;
+
+/**
+ * Represents a set of timeout and retry policy options that may be specified for a table operation request.
+ */
+public class TableRequestOptions extends RequestOptions {
+ /**
+ * Reserved for internal use. Initializes the timeout and retry policy for this TableRequestOptions
+ * instance, if they are currently null
, using the values specified in the {@link CloudTableClient}
+ * parameter.
+ *
+ * @param client
+ * The {@link CloudTableClient} client object to copy the timeout and retry policy from.
+ */
+ protected void applyDefaults(final CloudTableClient client) {
+ super.applyBaseDefaults(client);
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResponse.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResponse.java
new file mode 100644
index 0000000000000..848e7b2afc0bc
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResponse.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.net.HttpURLConnection;
+
+import com.microsoft.windowsazure.services.core.storage.ResultContinuation;
+import com.microsoft.windowsazure.services.core.storage.ResultContinuationType;
+
+/**
+ * Reserved for internal use. A class used to help parse responses from the Table service.
+ */
+class TableResponse {
+ /**
+ * Reserved for internal use. A static factory method that constructs a {@link ResultContinuation} instance from the
+ * continuation token information in a table operation response, if any.
+ *
+ * @param queryRequest
+ * The java.net.HttpURLConnection
request response to parse for continuation token
+ * information.
+ *
+ * @return
+ * A {@link ResultContinuation} instance from continuation token information in the response, or
+ *
null
if none is found.
+ */
+ protected static ResultContinuation getTableContinuationFromResponse(final HttpURLConnection queryRequest) {
+ final ResultContinuation retVal = new ResultContinuation();
+ retVal.setContinuationType(ResultContinuationType.TABLE);
+
+ boolean foundToken = false;
+
+ String tString = queryRequest.getHeaderField(TableConstants.TABLE_SERVICE_PREFIX_FOR_TABLE_CONTINUATION
+ .concat(TableConstants.TABLE_SERVICE_NEXT_PARTITION_KEY));
+ if (tString != null) {
+ retVal.setNextPartitionKey(tString);
+ foundToken = true;
+ }
+
+ tString = queryRequest.getHeaderField(TableConstants.TABLE_SERVICE_PREFIX_FOR_TABLE_CONTINUATION
+ .concat(TableConstants.TABLE_SERVICE_NEXT_ROW_KEY));
+ if (tString != null) {
+ retVal.setNextRowKey(tString);
+ foundToken = true;
+ }
+
+ tString = queryRequest.getHeaderField(TableConstants.TABLE_SERVICE_PREFIX_FOR_TABLE_CONTINUATION
+ .concat(TableConstants.TABLE_SERVICE_NEXT_MARKER));
+ if (tString != null) {
+ retVal.setNextMarker(tString);
+ foundToken = true;
+ }
+
+ tString = queryRequest.getHeaderField(TableConstants.TABLE_SERVICE_PREFIX_FOR_TABLE_CONTINUATION
+ .concat(TableConstants.TABLE_SERVICE_NEXT_TABLE_NAME));
+ if (tString != null) {
+ retVal.setNextTableName(tString);
+ foundToken = true;
+ }
+
+ return foundToken ? retVal : null;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResult.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResult.java
new file mode 100644
index 0000000000000..95e0621b63ed9
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableResult.java
@@ -0,0 +1,179 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.util.HashMap;
+
+/**
+ * A class which represents the result of a table operation. The {@link TableResult} class encapsulates the HTTP
+ * response
+ * and any table entity results returned by the Storage Service REST API operation called for a particular
+ * {@link TableOperation}.
+ *
+ */
+public class TableResult {
+ private Object result;
+
+ private int httpStatusCode = -1;
+
+ private String id;
+
+ private String etag;
+
+ private HashMapString
containing the Etag returned by the server with the table operation results.
+ */
+ public String getEtag() {
+ return this.etag;
+ }
+
+ /**
+ * Gets the HTTP status code returned by a table operation request.
+ *
+ * @return
+ * The HTTP status code for the table operation returned by the server.
+ */
+ public int getHttpStatusCode() {
+ return this.httpStatusCode;
+ }
+
+ /**
+ * Gets the AtomPub Entry Request ID value for the result returned by a table operation request.
+ *
+ * @return
+ * The Entry Request ID for the table operation result.
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * Gets the map of properties for a table entity returned by the table operation.
+ *
+ * @return
+ * A java.util.HashMap
of String
property names to {@link EntityProperty} data
+ * typed values representing the properties of a table entity.
+ */
+ public HashMapObject
.
+ */
+ public Object getResult() {
+ return this.result;
+ }
+
+ /**
+ * Gets the result returned by the table operation as an instance of the specified type.
+ *
+ * @return
+ * The result returned by the table operation as an instance of type T
.
+ */
+ @SuppressWarnings("unchecked")
+ public String
containing an Etag to associate with the table operation results.
+ */
+ protected void setEtag(final String etag) {
+ this.etag = etag;
+ }
+
+ /**
+ * Reserved for internal use. Sets the HTTP status code associated with the table operation results.
+ *
+ * @param httpStatusCode
+ * The HTTP status code value to associate with the table operation results.
+ */
+ protected void setHttpStatusCode(final int httpStatusCode) {
+ this.httpStatusCode = httpStatusCode;
+ }
+
+ /**
+ * Reserved for internal use. Sets the AtomPub Entry Request ID associated with the table operation result.
+ *
+ * @param id
+ * A String
containing the request ID to associate with the table operation result.
+ */
+ protected void setId(final String id) {
+ this.id = id;
+ }
+
+ /**
+ * Reserved for internal use. Sets the map of properties for a table entity to associate with the table operation.
+ *
+ * @param properties
+ * A java.util.HashMap
of String
property names to {@link EntityProperty} data
+ * typed values representing the properties of a table entity to associate with the table operation.
+ */
+ protected void setProperties(final HashMapObject
to associate with the table operation.
+ */
+ protected void setResult(final Object result) {
+ this.result = result;
+ }
+
+ /**
+ * Reserved for internal use. Sets the result to associate with the table operation as a {@link TableEntity}.
+ *
+ * @param ent
+ * An instance of an object implementing {@link TableEntity} to associate with the table operation.
+ */
+ protected void updateResultObject(final TableEntity ent) {
+ this.result = ent;
+ ent.setEtag(this.etag);
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableServiceEntity.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableServiceEntity.java
new file mode 100644
index 0000000000000..d958bb328444b
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableServiceEntity.java
@@ -0,0 +1,414 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+import com.microsoft.windowsazure.services.core.storage.Constants;
+import com.microsoft.windowsazure.services.core.storage.OperationContext;
+import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+
+/**
+ * The {@link TableServiceEntity} class represents the base object type for a table entity in the Storage service.
+ * {@link TableServiceEntity} provides a base implementation for the {@link TableEntity} interface that provides
+ * readEntity
and writeEntity
methods that by default serialize and deserialize all properties
+ * via reflection. A table entity class may extend this class and override the readEntity
and
+ * writeEntity
methods to provide customized or more performant serialization logic.
+ * public type getPropertyName() { ... }
+ * public void setPropertyName(type parameter) { ... }
+ * name
attribute to specify a property name for
+ * reflection on getter and setter methods that do not follow the property name convention. Method names and the
+ * name
attribute of {@link StoreAs} annotations are case sensitive for matching property names with
+ * reflection. Use the {@link Ignore} annotation to prevent methods from being used by reflection for automatic
+ * serialization and deserialization. Note that the names "PartitionKey", "RowKey", "Timestamp", and "Etag" are reserved
+ * and will be ignored if set with the {@link StoreAs} annotation in a subclass.
+ *
+ *
+ *
+ *
+ * Storage Type
+ * EdmType Value
+ * Java Type
+ * Description
+ *
+ *
+ * Edm.Binary
+ * {@link EdmType#BINARY}
+ *
+ * byte[], Byte[]
An array of bytes up to 64 KB in size.
+ *
+ *
+ * Edm.Boolean
+ * {@link EdmType#BOOLEAN}
+ *
+ * boolean, Boolean
A Boolean value.
+ *
+ *
+ * Edm.Byte
+ * {@link EdmType#BYTE}
+ *
+ * boolean, Boolean
A Boolean value.
+ *
+ *
+ * Edm.DateTime
+ * {@link EdmType#DATE_TIME}
+ *
+ * Date
A 64-bit value expressed as Coordinated Universal Time (UTC). The supported range begins from 12:00 midnight,
+ * January 1, 1601 A.D. (C.E.), UTC. The range ends at December 31, 9999.
+ *
+ *
+ * Edm.Double
+ * {@link EdmType#DOUBLE}
+ *
+ * double, Double
A 64-bit double-precision floating point value.
+ *
+ *
+ * Edm.Guid
+ * {@link EdmType#GUID}
+ *
+ * UUID
A 128-bit globally unique identifier.
+ *
+ *
+ * Edm.Int32
+ * {@link EdmType#INT32}
+ *
+ * int, Integer
A 32-bit integer value.
+ *
+ *
+ * Edm.Int64
+ * {@link EdmType#INT64}
+ *
+ * long, Long
A 64-bit integer value.
+ *
+ *
+ * Edm.String
+ * {@link EdmType#STRING}
+ *
+ * String
A UTF-16-encoded value. String values may be up to 64 KB in size.
+ * String
property names to {@link EntityProperty} objects containing typed data
+ * values to deserialize into the instance parameter object.
+ * @param opContext
+ * An {@link OperationContext} object that represents the context for the current operation.
+ *
+ * @throws IllegalArgumentException
+ * if the table entity response received is invalid or improperly formatted.
+ * @throws IllegalAccessException
+ * if the table entity threw an exception during deserialization.
+ * @throws InvocationTargetException
+ * if a method invoked on the instance parameter threw an exception during deserialization.
+ */
+ public static void readEntityWithReflection(final Object instance,
+ final HashMapString
property names to {@link EntityProperty} objects containing typed data
+ * values serialized from the instance parameter object.
+ *
+ * @throws IllegalArgumentException
+ * if the table entity is invalid or improperly formatted.
+ * @throws IllegalAccessException
+ * if the table entity threw an exception during serialization.
+ * @throws InvocationTargetException
+ * if a method invoked on the instance parameter threw an exception during serialization.
+ */
+ public static HashMapString
containing the Etag for the entity.
+ */
+ @Override
+ public String getEtag() {
+ return this.etag;
+ }
+
+ /**
+ * Gets the PartitionKey value for the entity.
+ *
+ * @return
+ * A String
containing the PartitionKey value for the entity.
+ */
+ @Override
+ public String getPartitionKey() {
+ return this.partitionKey;
+ }
+
+ /**
+ * Gets the RowKey value for the entity.
+ *
+ * @return
+ * A String
containing the RowKey value for the entity.
+ */
+ @Override
+ public String getRowKey() {
+ return this.rowKey;
+ }
+
+ /**
+ * Gets the Timestamp value for the entity.
+ *
+ * @return
+ * A Date
containing the Timestamp value for the entity.
+ */
+ @Override
+ public Date getTimestamp() {
+ return this.timeStamp;
+ }
+
+ /**
+ * Populates this table entity instance using the map of property names to {@link EntityProperty} data typed values.
+ * java.util.HashMap
of String
property names to {@link EntityProperty}
+ * data values to deserialize and store in this table entity instance.
+ * @param opContext
+ * An {@link OperationContext} object used to track the execution of the operation.
+ * @throws StorageException
+ * if an error occurs during the deserialization.
+ */
+ @Override
+ public void readEntity(final HashMapString
containing the Etag for the entity.
+ */
+ @Override
+ public void setEtag(final String etag) {
+ this.etag = etag;
+ }
+
+ /**
+ * Sets the PartitionKey value for the entity.
+ *
+ * @param partitionKey
+ * A String
containing the PartitionKey value for the entity.
+ */
+ @Override
+ public void setPartitionKey(final String partitionKey) {
+ this.partitionKey = partitionKey;
+ }
+
+ /**
+ * Sets the RowKey value for the entity.
+ *
+ * @param rowKey
+ * A String
containing the RowKey value for the entity.
+ */
+ @Override
+ public void setRowKey(final String rowKey) {
+ this.rowKey = rowKey;
+ }
+
+ /**
+ * Sets the Timestamp value for the entity.
+ *
+ * @param timeStamp
+ * A Date
containing the Timestamp value for the entity.
+ */
+ @Override
+ public void setTimestamp(final Date timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Returns a map of property names to {@link EntityProperty} data typed values created by serializing this table
+ * entity instance.
+ * java.util.HashMap
of String
property names to {@link EntityProperty} data
+ * typed values representing the properties serialized from this table entity instance.
+ * @throws StorageException
+ * if an error occurs during the serialization.
+ */
+ @Override
+ public HashMapjava.io.InputStream
of the error response from the table operation request.
+ * @return
+ * A {@link TableServiceException} instance initialized with values from the input parameters.
+ * @throws IOException
+ * if an IO error occurs.
+ */
+ protected static TableServiceException generateTableServiceException(boolean retryable, RequestResult res,
+ TableOperation op, InputStream inStream) throws IOException {
+ try {
+ TableServiceException retryableException = new TableServiceException(res.getStatusCode(),
+ res.getStatusMessage(), op, new InputStreamReader(inStream));
+ retryableException.retryable = retryable;
+
+ return retryableException;
+ }
+ finally {
+ inStream.close();
+ }
+ }
+
+ private TableOperation operation;
+
+ /**
+ * Reserved for internal use. This flag indicates whether the operation that threw the exception can be retried.
+ */
+ protected boolean retryable = false;
+
+ /**
+ * Constructs a TableServiceException
instance using the specified error code, message, status code,
+ * extended error information and inner exception.
+ *
+ * @param errorCode
+ * A String
that represents the error code returned by the table operation.
+ * @param message
+ * A String
that represents the error message returned by the table operation.
+ * @param statusCode
+ * The HTTP status code returned by the table operation.
+ * @param extendedErrorInfo
+ * A {@link StorageExtendedErrorInformation} object that represents the extended error information
+ * returned by the table operation.
+ * @param innerException
+ * An Exception
object that represents a reference to the initial exception, if one exists.
+ */
+ public TableServiceException(final String errorCode, final String message, final int statusCode,
+ final StorageExtendedErrorInformation extendedErrorInfo, final Exception innerException) {
+ super(errorCode, message, statusCode, extendedErrorInfo, innerException);
+ }
+
+ /**
+ * Reserved for internal use. Constructs a TableServiceException
instance using the specified HTTP
+ * status code, message, operation, and stream reader.
+ *
+ * @param httpStatusCode
+ * The int
HTTP Status Code value returned by the table operation that caused the exception.
+ * @param message
+ * A String
description of the error that caused the exception.
+ * @param operation
+ * The {@link TableOperation} object representing the table operation that was in progress when the
+ * exception occurred.
+ * @param reader
+ * The Java.IO.Stream
derived stream reader for the HTTP request results returned by the
+ * table operation, if any.
+ */
+ protected TableServiceException(final int httpStatusCode, final String message, final TableOperation operation,
+ final Reader reader) {
+ super(null, message, httpStatusCode, null, null);
+ this.operation = operation;
+
+ if (reader != null) {
+ try {
+ final StorageErrorResponse error = new StorageErrorResponse(reader);
+ this.extendedErrorInformation = error.getExtendedErrorInformation();
+ this.errorCode = this.extendedErrorInformation.getErrorCode();
+ }
+ catch (XMLStreamException e) {
+ // no-op, if error parsing fails, just throw first exception.
+ }
+ }
+ }
+
+ /**
+ * Gets the table operation that caused the TableServiceException
to be thrown.
+ *
+ * @return
+ * The {@link TableOperation} object representing the table operation that caused this
+ * {@link TableServiceException} to be thrown.
+ */
+ public TableOperation getOperation() {
+ return this.operation;
+ }
+
+ /**
+ * Reserved for internal use. Gets a flag indicating the table operation can be retried.
+ *
+ * @return
+ * The boolean
flag indicating whether the table operation that caused the exception can be
+ * retried.
+ */
+ public boolean isRetryable() {
+ return this.retryable;
+ }
+
+ /**
+ * Reserved for internal use. Sets the table operation that caused the TableServiceException
to be
+ * thrown.
+ *
+ * @param operation
+ * The {@link TableOperation} object representing the table operation that caused this
+ * {@link TableServiceException} to be thrown.
+ */
+ protected void setOperation(final TableOperation operation) {
+ this.operation = operation;
+ }
+
+ /**
+ * Reserved for internal use. Sets a flag indicating the table operation can be retried.
+ *
+ * @param retryable
+ * The boolean
flag to set indicating whether the table operation that caused the exception
+ * can be retried.
+ */
+ protected void setRetryable(boolean retryable) {
+ this.retryable = retryable;
+ }
+}
diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableUpdateType.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableUpdateType.java
new file mode 100644
index 0000000000000..3fbf519729c44
--- /dev/null
+++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/TableUpdateType.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+/**
+ * Reserved for internal use. An enum that represents the type of update a given upsert operation will perform.
+ */
+enum TableUpdateType {
+ /**
+ * The table operation updates an existing entity.
+ */
+ MERGE,
+
+ /**
+ * The table operation replaces an existing entity.
+ */
+ REPLACE;
+}
diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java
new file mode 100644
index 0000000000000..671ac7c308a75
--- /dev/null
+++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java
@@ -0,0 +1,762 @@
+/**
+ * Copyright 2011 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.windowsazure.services.table.client;
+
+import static org.junit.Assert.*;
+
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.microsoft.windowsazure.services.core.storage.StorageException;
+
+public class TableBatchOperationTests extends TableTestBase {
+ @Test
+ public void batchDelete() throws StorageException {
+ TableBatchOperation batch = new TableBatchOperation();
+
+ // insert entity
+ class1 ref = generateRandomEnitity("jxscl_odata");
+ tClient.execute(testSuiteTableName, TableOperation.insert(ref));
+ batch.delete(ref);
+
+ ArrayList