com.microsoft.azure.storage.table.TableServiceEntity Maven / Gradle / Ivy
Show all versions of azure-storage Show documentation
/**
* Copyright Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.azure.storage.table;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import com.microsoft.azure.storage.Constants;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.StorageErrorCodeStrings;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.core.SR;
/**
* 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.
*
* 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 Microsoft Azure table storage. The reflection
* code looks for getter and setter methods in pairs of the form
*
* public type getPropertyName() { ... }
*
* and
*
* public void setPropertyName(type parameter) { ... }
*
* 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 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.
*
* The following table shows the supported property data types in Microsoft Azure storage and the corresponding Java
* types when deserialized.
*
* Supported property data types in Microsoft Azure storage
*
* 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.DateTime
* {@link EdmType#DATE_TIME}
* java.util.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.
*
*
*
* See the MSDN topic Understanding the Table Service
* Data Model for an overview of tables, entities, and properties as used in the Microsoft 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
* An Object
reference to an instance of a class implementing {@link TableEntity} to
* deserialize the table entity
* data into.
* @param properties
* A java.util.HashMap
object which maps 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 HashMap properties, final OperationContext opContext)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
final HashMap props = PropertyPair.generatePropertyPairs(instance.getClass());
for (final Entry p : properties.entrySet()) {
if (props.containsKey(p.getKey())) {
props.get(p.getKey()).consumeEntityProperty(p.getValue(), instance);
}
}
}
/**
* Serializes the property data from a table entity instance into a property map 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 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
* An Object
reference to an instance of a class implementing {@link TableEntity} to
* serialize the table entity
* data from.
* @return
* A java.util.HashMap
object which maps String
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 HashMap writeEntityWithReflection(final Object instance)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
final HashMap props = PropertyPair.generatePropertyPairs(instance.getClass());
final HashMap retVal = new HashMap();
for (final Entry p : props.entrySet()) {
retVal.put(p.getValue().effectiveName, p.getValue().generateEntityProperty(instance));
}
return retVal;
}
/**
* The default to multiply the number of CPU's by to get the number of threads to allow in the
* ReflectedEntityCache
*/
private static final int DEFAULT_CONCURRENCY_MULTIPLIER = 4;
/**
* The default load factor for the ReflectedEntityCache
*/
private static final float DEFAULT_LOAD_FACTOR = (float) .75;
/**
* The default initial capacity for the ReflectedEntityCache
*/
private static final int DEFAULT_INITIAL_CAPACITY = 31;
/**
* The reflected entity cache stores known entity types and their respective reflected entity dictionaries. Rather
* than using reflection on a known entity type, the values from the dictionary are used instead.
*/
private static boolean disableReflectedEntityCache = false;
/**
* Reserved for internal use. The value of the partition key in the entity.
*/
protected String partitionKey = null;
/**
* Reserved for internal use. The value of the row key in the entity.
*/
protected String rowKey = null;
/**
* Reserved for internal use. The value of the ETag for the entity.
*/
protected String etag = null;
/**
* Reserved for internal use. The value of the Timestamp in the entity.
*/
protected Date timeStamp = new Date();
/**
* Initializes an empty {@link TableServiceEntity} instance.
*/
public TableServiceEntity() {
// Empty ctor
}
/**
* Initializes a new instance of the {@link TableServiceEntity} class with the specified partition key and row key.
*
* @param partitionKey
* A String
which represents the partition key of the {@link TableServiceEntity} to be
* initialized.
* @param rowKey
* A String
which represents the row key of the {@link TableServiceEntity} to be
* initialized.
*/
public TableServiceEntity(String partitionKey, String rowKey) {
this.partitionKey = partitionKey;
this.rowKey = rowKey;
}
/**
* Gets the ETag value to verify for the entity. This value is used to determine if the table entity has changed
* since it was last read from Microsoft Azure storage. The client cannot update this value on the service.
*
* @return
* A String
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 for the entity. The server manages the value of Timestamp, which cannot be modified.
*
* @return
* A java.util.Date
object which represents the Timestamp value for the entity.
*/
@Override
public Date getTimestamp() {
return this.timeStamp;
}
/**
* Gets a value indicating whether or not the reflected entity cache is disabled. For most scenarios, disabling
* the reflected entity cache is not recommended due to its effect on performance.
*
* The reflected entity cache stores known entity types and their respective reflected entity dictionaries. Rather
* than using reflection on a known entity type, the values from the dictionary are used instead.
*
* @return
* true
if the reflected entity cache is disabled; otherwise, false
.
*/
public static boolean isReflectedEntityCacheDisabled() {
return TableServiceEntity.disableReflectedEntityCache;
}
/**
* Sets a boolean representing whether or not the reflected entity cache is disabled. For most scenarios, disabling
* the reflected entity cache is not recommended due to its effect on performance.
*
* The reflected entity cache stores known entity types and their respective reflected entity dictionaries. Rather
* than using reflection on a known entity type, the values from the dictionary are used instead.
*
* @param disableReflectedEntityCache
* true
to disable the reflected entity cache; otherwise, false
.
*/
public static void setReflectedEntityCacheDisabled(boolean disableReflectedEntityCache) {
if (TableServiceEntity.reflectedEntityCache != null && disableReflectedEntityCache) {
TableServiceEntity.reflectedEntityCache.clear();
}
TableServiceEntity.disableReflectedEntityCache = disableReflectedEntityCache;
}
/**
* Populates this table entity instance using the map of property names to {@link EntityProperty} data typed values.
*
* 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 Microsoft Azure storage the class represents.
*
* Override this method in classes that extend {@link TableServiceEntity} to invoke custom serialization code.
*
* @param properties
* The 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 HashMap properties, final OperationContext opContext)
throws StorageException {
try {
readEntityWithReflection(this, properties, opContext);
}
catch (IllegalArgumentException e) {
throw new StorageException(StorageErrorCodeStrings.INVALID_DOCUMENT, SR.RESPONSE_RECEIVED_IS_INVALID,
Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
}
catch (IllegalAccessException e) {
throw new StorageException(StorageErrorCodeStrings.INVALID_DOCUMENT,
SR.EXCEPTION_THROWN_DURING_DESERIALIZATION, Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
}
catch (InvocationTargetException e) {
throw new StorageException(StorageErrorCodeStrings.INTERNAL_ERROR,
SR.EXCEPTION_THROWN_DURING_DESERIALIZATION, Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
}
}
/**
* Sets the ETag value to verify for the entity. This value is used to determine if the table entity has changed
* since it was last read from Microsoft Azure storage. The client cannot update this value on the service.
*
* @param etag
* A String
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. Note that the timestamp property is a read-only property,
* set by the service only.
*
* @param timeStamp
* A java.util.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.
*
* 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
* Microsoft 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 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 HashMap writeEntity(final OperationContext opContext) throws StorageException {
try {
return writeEntityWithReflection(this);
}
catch (final IllegalAccessException e) {
throw new StorageException(StorageErrorCodeStrings.INTERNAL_ERROR,
SR.ATTEMPTED_TO_SERIALIZE_INACCESSIBLE_PROPERTY, Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
}
catch (final InvocationTargetException e) {
throw new StorageException(StorageErrorCodeStrings.INTERNAL_ERROR,
SR.EXCEPTION_THROWN_DURING_SERIALIZATION, Constants.HeaderConstants.HTTP_UNUSED_306, null, e);
}
}
/**
* The reflected entity cache caches known entity types and their respective reflected entity dictionaries when
* entities are deserialized and the payload does not include JSON metadata.
*/
private static ConcurrentHashMap, HashMap> reflectedEntityCache = initialize();
private static ConcurrentHashMap, HashMap> initialize() {
Runtime runtime = Runtime.getRuntime();
int numberOfProcessors = runtime.availableProcessors();
return new ConcurrentHashMap, HashMap>(DEFAULT_INITIAL_CAPACITY,
DEFAULT_LOAD_FACTOR, numberOfProcessors * DEFAULT_CONCURRENCY_MULTIPLIER);
}
/**
* The reflected entity cache caches known entity types and their respective reflected entity dictionaries when
* entities are deserialized and the payload does not include JSON metadata.
*
* @return
* The ConcurrentHashMap<Class<?>, HashMap<String, PropertyPair>>
representing
* the known entity types and their reflected entity dictionaries
*/
protected static ConcurrentHashMap, HashMap> getReflectedEntityCache() {
return TableServiceEntity.reflectedEntityCache;
}
}