com.linkedin.restli.client.response.BatchKVResponse Maven / Gradle / Ivy
/*
Copyright (c) 2012 LinkedIn Corp.
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.linkedin.restli.client.response;
import com.linkedin.data.DataMap;
import com.linkedin.data.schema.MapDataSchema;
import com.linkedin.data.schema.Name;
import com.linkedin.data.schema.RecordDataSchema;
import com.linkedin.data.template.DataTemplateUtil;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.internal.common.util.CollectionUtils;
import com.linkedin.restli.common.ComplexKeySpec;
import com.linkedin.restli.common.ComplexResourceKey;
import com.linkedin.restli.common.CompoundKey;
import com.linkedin.restli.common.ErrorResponse;
import com.linkedin.restli.common.ProtocolVersion;
import com.linkedin.restli.common.TypeSpec;
import com.linkedin.restli.internal.common.AllProtocolVersions;
import com.linkedin.restli.internal.common.ResponseUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* A batch of records. Used to return a fixed-size, unordered, complete collection of records, keyed on resource ID. Used
* as a response for a get_batch request.
*
* @author Keren Jin
*/
public class BatchKVResponse extends RecordTemplate
{
public static final String RESULTS = "results";
public static final String ERRORS = "errors";
private static final String BATCH_KV_RESPONSE_CLASSNAME = BatchKVResponse.class.getSimpleName();
private RecordDataSchema _schema;
private Class _valueClass;
private Map _results;
private Map _errors;
/**
* Constructor for collection and association resource responses. For complex key resources
* use the constructor that accepts keyKeyClass and keyParamsClass.
*
* @param data provides the batch response data.
* @param keyClass provides the class identifying the key type:
*
* - For collection resources must be a primitive or a typeref to a primitive.
* - For an association resources must be {@link CompoundKey} and keyParts must contain an entry for each association key field.
* - For complex resources do not use this constructor, use the one that accepts keyKeyClass and keyParamsClass.
*
* @param valueClass provides the entity type of the collection.
* @param keyParts provides a map for association keys of each key name to {@link CompoundKey.TypeInfo}, for non-association resources must be an empty map.
*/
public BatchKVResponse(DataMap data,
Class keyClass,
Class valueClass,
Map keyParts,
ProtocolVersion version)
{
this(data,
keyClass,
valueClass,
keyParts,
null,
null,
version);
}
@Deprecated
public BatchKVResponse(DataMap data,
Class keyClass,
Class valueClass,
Map keyParts)
{
this(data, keyClass, valueClass, keyParts, AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion());
}
/**
* Constructor for collection and association resource responses. For complex key resources
* use the constructor that accepts keyKeyClass and keyParamsClass.
*
* @param data provides the batch response data.
* @param keyType provides the class identifying the key type:
*
* - For collection resources must be a primitive or a typeref to a primitive.
* - For an association resources must be {@link CompoundKey} and keyParts must contain an entry for each association key field.
* - For complex resources do not use this constructor, use the one that accepts keyKeyClass and keyParamsClass.
*
* @param valueType provides the entity type of the collection.
* @param keyParts provides a map for association keys of each key name to {@link CompoundKey.TypeInfo}, for non-association resources must be an empty map.
*/
public BatchKVResponse(DataMap data,
TypeSpec keyType,
TypeSpec valueType,
Map keyParts,
ProtocolVersion version)
{
this(data, keyType, valueType, keyParts, null, version);
}
@Deprecated
public BatchKVResponse(DataMap data,
TypeSpec keyType,
TypeSpec valueType,
Map keyParts)
{
this(data, keyType, valueType, keyParts, AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion());
}
/**
* Constructor resource responses.
*
* @param data provides the batch response data.
* @param keyClass provides the class identifying the key type.
*
* - For collection resources must be a primitive or a typeref to a primitive.
* - For an association resources must be {@link CompoundKey} and keyParts must contain an entry for each association key field.
* - For complex resources must be {@link ComplexResourceKey}, keyKeyClass must contain the
* key's record template class and if the resource has a key params their record template type keyParamsClass must be provided.
* @param valueClass provides the entity type of the collection.
* @param keyParts provides a map for association keys of each key name to {@link CompoundKey.TypeInfo}, for non-association resources must be an empty map.
* @param keyKeyClass provides the record template class for the key for complex key resources, otherwise null.
* @param keyParamsClass provides the record template class for the key params for complex key resources, otherwise null.
*/
public BatchKVResponse(DataMap data,
Class keyClass,
Class valueClass,
Map keyParts,
Class extends RecordTemplate> keyKeyClass,
Class extends RecordTemplate> keyParamsClass,
ProtocolVersion version)
{
this(data,
TypeSpec.forClassMaybeNull(keyClass),
TypeSpec.forClassMaybeNull(valueClass),
keyParts,
ComplexKeySpec.forClassesMaybeNull(keyKeyClass, keyParamsClass),
version);
}
@Deprecated
public BatchKVResponse(DataMap data,
Class keyClass,
Class valueClass,
Map keyParts,
Class extends RecordTemplate> keyKeyClass,
Class extends RecordTemplate> keyParamsClass)
{
this(data, keyClass, valueClass, keyParts, keyKeyClass, keyParamsClass, AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion());
}
/**
* Constructor resource responses.
*
* @param data provides the batch response data.
* @param keyType provides the class identifying the key type.
*
* - For collection resources must be a primitive or a typeref to a primitive.
* - For an association resources must be {@link CompoundKey} and keyParts must contain an entry for each association key field.
* - For complex resources must be {@link ComplexResourceKey}, keyKeyClass must contain the
* key's record template class and if the resource has a key params their record template type keyParamsClass must be provided.
* @param valueType provides the entity type of the collection.
* @param keyParts provides a map for association keys of each key name to {@link CompoundKey.TypeInfo}, for non-association resources must be an empty map.
* @param complexKeyType provides the type of the key for complex key resources, otherwise null.
*/
public BatchKVResponse(DataMap data,
TypeSpec keyType,
TypeSpec valueType,
Map keyParts,
ComplexKeySpec, ?> complexKeyType,
ProtocolVersion version)
{
super(data, null);
_valueClass = valueType.getType();
deserializeData(keyType, keyParts, complexKeyType, version);
}
@Deprecated
public BatchKVResponse(DataMap data,
TypeSpec keyType,
TypeSpec valueType,
Map keyParts,
ComplexKeySpec, ?> complexKeyType)
{
this(data, keyType, valueType, keyParts, complexKeyType, AllProtocolVersions.RESTLI_PROTOCOL_1_0_0.getProtocolVersion());
}
protected BatchKVResponse(DataMap data)
{
super(data, null);
}
protected void createSchema(Class valueClass)
{
_valueClass = valueClass;
}
protected void deserializeData(TypeSpec keyType,
Map keyParts,
ComplexKeySpec, ?> complexKeyType,
ProtocolVersion version)
{
deserializeData(data(), keyType, keyParts, complexKeyType, version);
}
protected void deserializeData(DataMap data, TypeSpec keyType,
Map keyParts,
ComplexKeySpec, ?> complexKeyType,
ProtocolVersion version)
{
final DataMap resultsRaw = data.getDataMap(RESULTS);
if (resultsRaw == null)
{
_results = new ParamlessKeyHashMap(complexKeyType);
}
else
{
_results = new ParamlessKeyHashMap(
CollectionUtils.getMapInitialCapacity(resultsRaw.size(), 0.75f), 0.75f, complexKeyType);
for (Map.Entry entry : resultsRaw.entrySet())
{
@SuppressWarnings("unchecked")
final K key = (K) ResponseUtils.convertKey(entry.getKey(), keyType, keyParts, complexKeyType, version);
final V value = deserializeValue(entry.getValue());
_results.put(key, value);
}
}
final DataMap errorsRaw = data.getDataMap(ERRORS);
if (errorsRaw == null)
{
_errors = new ParamlessKeyHashMap(complexKeyType);
}
else
{
_errors = new ParamlessKeyHashMap(
CollectionUtils.getMapInitialCapacity(errorsRaw.size(), 0.75f), 0.75f, complexKeyType);
for (Map.Entry entry : errorsRaw.entrySet())
{
@SuppressWarnings("unchecked")
final K key = (K) ResponseUtils.convertKey(entry.getKey(), keyType, keyParts, complexKeyType, version);
final ErrorResponse value = DataTemplateUtil.wrap(entry.getValue(), ErrorResponse.class);
_errors.put(key, value);
}
}
}
/**
* Returns the results of batch operation. Please note differences between Rest.li protocol before and after 2.0
* @return
* For Rest.li protocol ver. < 2.0: entries which succeeded
* For Rest.li protocol ver. >= 2.0: all entries as EntityResponse, including successful and failed ones.
*/
public Map getResults()
{
return _results;
}
/**
* Returns the errors of batch operation. Please note differences between Rest.li protocol before and after 2.0
* @return
* For Rest.li protocol ver. < 2.0: entries which failed
* For Rest.li protocol ver. >= 2.0: ignore, please use getResults() instead
*/
public Map getErrors()
{
return _errors;
}
@Override
public RecordDataSchema schema()
{
synchronized (this)
{
// Don't use double-checked locking because _schema isn't volatile
if (_schema == null)
{
final StringBuilder errorMessageBuilder = new StringBuilder(10);
final Name elementSchemaName = new Name(_valueClass.getSimpleName(), errorMessageBuilder);
final MapDataSchema resultsSchema = new MapDataSchema(new RecordDataSchema(elementSchemaName, RecordDataSchema.RecordType.RECORD));
final RecordDataSchema.Field resultsField = new RecordDataSchema.Field(resultsSchema);
resultsField.setName(RESULTS, errorMessageBuilder);
final MapDataSchema errorsSchema = new MapDataSchema(DataTemplateUtil.getSchema(ErrorResponse.class));
final RecordDataSchema.Field errorsField = new RecordDataSchema.Field(errorsSchema);
errorsField.setName(ERRORS, errorMessageBuilder);
final Name name = new Name(BATCH_KV_RESPONSE_CLASSNAME, errorMessageBuilder);
_schema = new RecordDataSchema(name, RecordDataSchema.RecordType.RECORD);
_schema.setFields(Arrays.asList(resultsField, errorsField), errorMessageBuilder);
}
}
return _schema;
}
protected V deserializeValue(Object valueData)
{
return DataTemplateUtil.wrap(valueData, _valueClass);
}
private class ParamlessKeyHashMap extends HashMap
{
private static final long serialVersionUID = 1L;
private final ComplexKeySpec, ?> _complexKeyType;
private ParamlessKeyHashMap(ComplexKeySpec, ?> complexKeyType)
{
_complexKeyType = complexKeyType;
}
private ParamlessKeyHashMap(int initialCapacity, float loadFactor, ComplexKeySpec, ?> complexKeyType)
{
super(initialCapacity, loadFactor);
_complexKeyType = complexKeyType;
}
@Override
public MV get(Object key)
{
return super.get(getProcessedKey(key));
}
@Override
public boolean containsKey(Object key)
{
return super.containsKey(getProcessedKey(key));
}
@Override
public MV put(K key, MV value)
{
return super.put(getProcessedKey(key), value);
}
@Override
public MV remove(Object key)
{
return super.remove(getProcessedKey(key));
}
@SuppressWarnings("unchecked")
private MK getProcessedKey(MK key)
{
if (key instanceof ComplexResourceKey)
{
final ComplexResourceKey complexKey = (ComplexResourceKey) key;
return (MK) new ComplexResourceKey(
complexKey.getKey(),
DataTemplateUtil.wrap(new DataMap(), _complexKeyType.getParamsType().getType()));
}
else
{
return key;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy