/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.gora.dynamodb.store;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.gora.dynamodb.query.DynamoDBKey;
import org.apache.gora.dynamodb.query.DynamoDBQuery;
import org.apache.gora.dynamodb.query.DynamoDBResult;
import org.apache.gora.dynamodb.store.DynamoDBMapping.DynamoDBMappingBuilder;
import org.apache.gora.persistency.BeanFactory;
import org.apache.gora.persistency.Persistent;
import org.apache.gora.query.PartitionQuery;
import org.apache.gora.query.Query;
import org.apache.gora.query.Result;
import org.apache.gora.store.ws.impl.WSDataStoreBase;
import org.apache.gora.util.GoraException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.dynamodb.AmazonDynamoDB;
import com.amazonaws.services.dynamodb.AmazonDynamoDBAsyncClient;
import com.amazonaws.services.dynamodb.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodb.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodb.datamodeling.DynamoDBQueryExpression;
import com.amazonaws.services.dynamodb.datamodeling.DynamoDBScanExpression;
import com.amazonaws.services.dynamodb.model.CreateTableRequest;
import com.amazonaws.services.dynamodb.model.DeleteTableRequest;
import com.amazonaws.services.dynamodb.model.DeleteTableResult;
import com.amazonaws.services.dynamodb.model.DescribeTableRequest;
import com.amazonaws.services.dynamodb.model.KeySchema;
import com.amazonaws.services.dynamodb.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodb.model.ResourceNotFoundException;
import com.amazonaws.services.dynamodb.model.TableDescription;
import com.amazonaws.services.dynamodb.model.TableStatus;
public class DynamoDBStore<K, T extends Persistent> extends WSDataStoreBase<K, T> {
/**
* Helper to write useful information into the logs
*/
public static final Logger LOG = LoggerFactory.getLogger(DynamoDBStore.class);
/**
* Schema name which will be used from within the data store.
* If not set, all the available schemas from the mapping file will be used.
*/
private static String preferredSchema;
/**
* The mapping file to create the tables from
*/
private static final String MAPPING_FILE = "gora-dynamodb-mapping.xml";
/**
* Default times to wait while requests are performed
*/
private static long waitTime = 10L * 60L * 1000L;
private static long sleepTime = 1000L * 20L;
private static long sleepDeleteTime = 1000L * 10L;
/**
* AWS Credential file name.
*/
private static String awsCredentialsProperties = "AwsCredentials.properties";
/**
* Name of the cloud database provider.
*/
private static String wsProvider = "Amazon.Web.Services";
/**
* Parameter to decide what type of Amazon DynamoDB client to use
*/
private static String CLI_TYP_PROP = "gora.dynamodb.client";
/**
* Parameter to decide where the data store will make its computations
*/
private static String ENDPOINT_PROP = "gora.dynamodb.endpoint";
/**
* Parameter to decide which schema will be used
*/
private static String PREF_SCH_NAME = "preferred.schema.name";
/**
* Parameter to decide how reads will be made i.e. using strong consistency or eventual consistency.
*/
private static String CONSISTENCY_READS = "gora.dynamodb.consistent.reads";
/**
* The mapping object that contains the mapping file
*/
private DynamoDBMapping mapping;
/**
* Amazon DynamoDB client which can be asynchronous or nor
*/
private AmazonDynamoDB dynamoDBClient;
/**
* Contains the consistency level to be used
*/
private String consistency;
/**
* TODO This would be useful for the batch read/write operations
* Contains the elements to be written or read from the data store
*/
//private Map<K, T> buffer = new LinkedHashMap<K, T>();
/**
* The class that will be persisted
*/
Class<T> persistentClass;
/**
* Constructor
*/
public DynamoDBStore(){
}
/**
* Initialize the data store by reading the credentials, setting the cloud provider,
* setting the client's properties up, setting the end point and reading the mapping file
*/
public void initialize(Class<K> keyClass, Class<T> pPersistentClass,
Properties properties) {
try {
LOG.debug("Initializing DynamoDB store");
getCredentials();
setWsProvider(wsProvider);
preferredSchema = properties.getProperty(PREF_SCH_NAME);
dynamoDBClient = getClient(properties.getProperty(CLI_TYP_PROP),(AWSCredentials)getConf());
dynamoDBClient.setEndpoint(properties.getProperty(ENDPOINT_PROP));
mapping = readMapping();
consistency = properties.getProperty(CONSISTENCY_READS);
persistentClass = pPersistentClass;
}
catch (Exception e) {
LOG.error("Error while initializing DynamoDB store");
LOG.error(e.getMessage());
LOG.error(e.getStackTrace().toString());
}
}
/**
* Method to create the specific client to be used
* @param clientType
* @param credentials
* @return
*/
public AmazonDynamoDB getClient(String clientType, AWSCredentials credentials){
if (clientType.equals("sync"))
return new AmazonDynamoDBClient(credentials);
if (clientType.equals("async"))
return new AmazonDynamoDBAsyncClient(credentials);
return null;
}
/**
* Reads the schema file and converts it into a data structure to be used
* @param pMapFile The schema file to be mapped into a table
* @return DynamoDBMapping Object containing all necessary information to create tables
* @throws IOException
*/
@SuppressWarnings("unchecked")
private DynamoDBMapping readMapping() throws IOException {
DynamoDBMappingBuilder mappingBuilder = new DynamoDBMappingBuilder();
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(getClass().getClassLoader().getResourceAsStream(MAPPING_FILE));
Element root = doc.getRootElement();
List<Element> tableElements = root.getChildren("table");
for(Element tableElement : tableElements) {
String tableName = tableElement.getAttributeValue("name");
long readCapacUnits = Long.parseLong(tableElement.getAttributeValue("readcunit"));
long writeCapacUnits = Long.parseLong(tableElement.getAttributeValue("writecunit"));
mappingBuilder.setTableName(tableName);
mappingBuilder.setProvisionedThroughput(tableName, readCapacUnits, writeCapacUnits);
LOG.debug("Basic table properties have been set: Name, and Provisioned throughput.");
// Retrieving key's features
List<Element> fieldElements = tableElement.getChildren("key");
for(Element fieldElement : fieldElements) {
String keyName = fieldElement.getAttributeValue("name");
String keyType = fieldElement.getAttributeValue("type");
String keyAttrType = fieldElement.getAttributeValue("att-type");
if(keyType.equals("hash"))
mappingBuilder.setHashKeySchema(tableName, keyName, keyAttrType);
else if(keyType.equals("hashrange"))
mappingBuilder.setHashRangeKeySchema(tableName, keyName, keyAttrType);
}
LOG.debug("Table key schemas have been set.");
// Retrieving attributes
fieldElements = tableElement.getChildren("attribute");
for(Element fieldElement : fieldElements) {
String attributeName = fieldElement.getAttributeValue("name");
String attributeType = fieldElement.getAttributeValue("type");
mappingBuilder.addAttribute(tableName, attributeName, attributeType, 0);
}
LOG.debug("Table attributes have been read.");
}
} catch(IOException ex) {
LOG.error("Error while performing xml mapping.");
ex.printStackTrace();
throw ex;
} catch(Exception ex) {
LOG.error("Error while performing xml mapping.");
ex.printStackTrace();
throw new IOException(ex);
}
return mappingBuilder.build();
}
/**
* Creates the AWSCredentials object based on the properties file.
* @return AWSCredentials object
* @throws FileNotFoundException
* @throws IllegalArgumentException
* @throws IOException
*/
private AWSCredentials getCredentials() throws FileNotFoundException,
IllegalArgumentException, IOException {
if(authentication == null){
InputStream awsCredInpStr = getClass().getClassLoader().getResourceAsStream(awsCredentialsProperties);
if (awsCredInpStr == null)
LOG.info("AWS Credentials File was not found on the classpath!");
AWSCredentials credentials = new PropertiesCredentials(awsCredInpStr);
setConf(credentials);
}
return (AWSCredentials)authentication;
}
/**
* Builds a DynamoDB query from a generic Query object
* @param query Generic query object
* @return DynamoDBQuery
*/
private DynamoDBQuery<K, T> buildDynamoDBQuery(Query<K, T> query){
if(getSchemaName() == null) throw new IllegalStateException("There is not a preferred schema defined.");
DynamoDBQuery<K, T> dynamoDBQuery = new DynamoDBQuery<K, T>();
dynamoDBQuery.setKeySchema(mapping.getKeySchema(getSchemaName()));
dynamoDBQuery.setQuery(query);
dynamoDBQuery.setConsistencyReadLevel(getConsistencyReads());
dynamoDBQuery.buildExpression();
return dynamoDBQuery;
}
/**
* Gets consistency level for reads
* @return True for strong consistency or false for eventual consistent reads
*/
private boolean getConsistencyReads(){
if(consistency != null)
if(consistency.equals("true"))
return true;
return false;
}
/**
* Executes a query after building a DynamoDB specific query based on the received one
*/
@Override
public Result<K, T> execute(Query<K, T> query) {
DynamoDBQuery<K, T> dynamoDBQuery = buildDynamoDBQuery(query);
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
List<T> objList = null;
if (DynamoDBQuery.getType().equals(DynamoDBQuery.RANGE_QUERY))
objList = mapper.query(persistentClass, (DynamoDBQueryExpression)dynamoDBQuery.getQueryExpression());
if (DynamoDBQuery.getType().equals(DynamoDBQuery.SCAN_QUERY))
objList = mapper.scan(persistentClass, (DynamoDBScanExpression)dynamoDBQuery.getQueryExpression());
return new DynamoDBResult<K, T>(this, query, objList);
}
@Override
public T get(K key, String[] fields) {
/* DynamoDBQuery<K,T> query = new DynamoDBQuery<K,T>();
query.setDataStore(this);
//query.setKeyRange(key, key);
//query.setFields(fields);
//query.setLimit(1);
Result<K,T> result = execute(query);
boolean hasResult = result.next();
return hasResult ? result.get() : null;*/
return null;
}
@Override
/**
* Gets the object with the specific key
* @throws IOException
*/
public T get(K key) {
T object = null;
try {
Object rangeKey;
rangeKey = getRangeKey(key);
Object hashKey = getHashKey(key);
if (hashKey != null){
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
if (rangeKey != null)
object = mapper.load(persistentClass, hashKey, rangeKey);
else
object = mapper.load(persistentClass, hashKey);
}
else
throw new GoraException("Error while retrieving keys from object: " + key.toString());
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (GoraException ge){
LOG.error(ge.getMessage());
LOG.error(ge.getStackTrace().toString());
}
return object;
}
/**
* Creates a new DynamoDBQuery
*/
public Query<K, T> newQuery() {
Query<K,T> query = new DynamoDBQuery<K, T>(this);
//query.setFields(getFieldsToQuery(null));
return query;
}
/**
* Gets the preferred schema
*/
public String getSchemaName() {
if (preferredSchema != null)
return preferredSchema;
return null;
}
/**
* Sets the preferred schema
* @param pSchemaName
*/
public void setSchemaName(String pSchemaName){
preferredSchema = pSchemaName;
}
/**
* Creates the table within the data store for a preferred schema or
* for a group of schemas defined withing the mapping file
* @throws IOException
*/
@Override
public void createSchema() {
LOG.info("Creating schema");
if (mapping.getTables().isEmpty()) throw new IllegalStateException("There are not tables defined.");
if (preferredSchema == null){
LOG.debug("create schemas");
// read the mapping object
for(String tableName : mapping.getTables().keySet())
executeCreateTableRequest(tableName);
LOG.debug("tables created successfully.");
}
else{
LOG.debug("create schema " + preferredSchema);
executeCreateTableRequest(preferredSchema);
}
}
/**
* Executes a create table request using the DynamoDB client and waits
* the default time until it's been created.
* @param tableName
*/
private void executeCreateTableRequest(String tableName){
CreateTableRequest createTableRequest = getCreateTableRequest(tableName,
mapping.getKeySchema(tableName), mapping.getProvisionedThroughput(tableName));
// use the client to perform the request
dynamoDBClient.createTable(createTableRequest).getTableDescription();
// wait for table to become active
waitForTableToBecomeAvailable(tableName);
LOG.info(tableName + "Schema now available");
}
/**
* Builds the necessary requests to create tables
* @param tableName
* @param keySchema
* @param proThrou
* @return
*/
private CreateTableRequest getCreateTableRequest(String tableName, KeySchema keySchema, ProvisionedThroughput proThrou){
CreateTableRequest createTableRequest = new CreateTableRequest();
createTableRequest.setTableName(tableName);
createTableRequest.setKeySchema(keySchema);
createTableRequest.setProvisionedThroughput(proThrou);
return createTableRequest;
}
/**
* Deletes all tables present in the mapping object.
* @throws IOException
*/
@Override
public void deleteSchema() {
if (mapping.getTables().isEmpty()) throw new IllegalStateException("There are not tables defined.");
if (preferredSchema == null){
LOG.debug("Delete schemas");
if (mapping.getTables().isEmpty()) throw new IllegalStateException("There are not tables defined.");
// read the mapping object
for(String tableName : mapping.getTables().keySet())
executeDeleteTableRequest(tableName);
LOG.debug("All schemas deleted successfully.");
}
else{
LOG.debug("create schema " + preferredSchema);
executeDeleteTableRequest(preferredSchema);
}
}
/**
* Executes a delete table request using the DynamoDB client
* @param tableName
*/
public void executeDeleteTableRequest(String pTableName){
try{
DeleteTableRequest deleteTableRequest = new DeleteTableRequest().withTableName(pTableName);
DeleteTableResult result = dynamoDBClient.deleteTable(deleteTableRequest);
waitForTableToBeDeleted(pTableName);
LOG.debug("Schema: " + result.getTableDescription() + " deleted successfully.");
}
catch(Exception e){
LOG.debug("Schema: " + pTableName + " deleted.");
e.printStackTrace();
}
}
/**
* Waits up to 6 minutes to confirm if a table has been deleted or not
* @param pTableName
*/
private void waitForTableToBeDeleted(String pTableName){
LOG.debug("Waiting for " + pTableName + " to be deleted.");
long startTime = System.currentTimeMillis();
long endTime = startTime + waitTime;
while (System.currentTimeMillis() < endTime) {
try {Thread.sleep(sleepDeleteTime);} catch (Exception e) {}
try {
DescribeTableRequest request = new DescribeTableRequest().withTableName(pTableName);
TableDescription tableDescription = dynamoDBClient.describeTable(request).getTable();
String tableStatus = tableDescription.getTableStatus();
LOG.debug(pTableName + " - current state: " + tableStatus);
} catch (AmazonServiceException ase) {
if (ase.getErrorCode().equalsIgnoreCase("ResourceNotFoundException") == true)
return;
ase.printStackTrace();
}
}
LOG.debug(pTableName + " deleted.");
}
/**
* Waits up to 6 minutes to confirm if a table has been created or not
* @param pTableName
*/
private void waitForTableToBecomeAvailable(String tableName) {
LOG.debug("Waiting for " + tableName + " to become available");
long startTime = System.currentTimeMillis();
long endTime = startTime + waitTime;
while (System.currentTimeMillis() < endTime) {
try {Thread.sleep(sleepTime);} catch (Exception e) {}
try {
DescribeTableRequest request = new DescribeTableRequest().withTableName(tableName);
TableDescription tableDescription = dynamoDBClient.describeTable(request).getTable();
String tableStatus = tableDescription.getTableStatus();
LOG.debug(tableName + " - current state: " + tableStatus);
if (tableStatus.equals(TableStatus.ACTIVE.toString())) return;
} catch (AmazonServiceException ase) {
if (ase.getErrorCode().equalsIgnoreCase("ResourceNotFoundException") == false) throw ase;
}
}
throw new RuntimeException("Table " + tableName + " never became active");
}
/**
* Verifies if the specified schemas exist
* @throws IOException
*/
@Override
public boolean schemaExists() {
LOG.info("Verifying schemas.");
TableDescription success = null;
if (mapping.getTables().isEmpty()) throw new IllegalStateException("There are not tables defined.");
if (preferredSchema == null){
LOG.debug("Verifying schemas");
if (mapping.getTables().isEmpty()) throw new IllegalStateException("There are not tables defined.");
// read the mapping object
for(String tableName : mapping.getTables().keySet()){
success = getTableSchema(tableName);
if (success == null) return false;
}
}
else{
LOG.info("Verifying schema " + preferredSchema);
success = getTableSchema(preferredSchema);
}
LOG.info("Finished verifying schemas.");
return (success != null)? true: false;
}
/**
* Retrieves the table description for the specific resource name
* @param tableName
* @return
*/
private TableDescription getTableSchema(String tableName){
TableDescription tableDescription = null;
try{
DescribeTableRequest describeTableRequest = new DescribeTableRequest().withTableName(tableName);
tableDescription = dynamoDBClient.describeTable(describeTableRequest).getTable();
}
catch(ResourceNotFoundException e){
LOG.error("Error while getting table schema: " + tableName);
return tableDescription;
}
return tableDescription;
}
/**
* Returns a new instance of the key object.
* @throws IOException
*/
@Override
public K newKey() {
// TODO Auto-generated method stub
return null;
}
/**
* Returns a new persistent object
* @throws IOException
*/
@Override
public T newPersistent() {
T obj = null;
try {
obj = persistentClass.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
}
/**
* Puts an object identified by a key
* @throws IOException
*/
@Override
public void put(K key, T obj) {
try{
Object rangeKey = getRangeKey(key);
Object hashKey = getHashKey(key);
// if the key does not have these attributes then try to get them from the object
if (hashKey == null)
hashKey = getHashKey(obj);
if (rangeKey == null)
rangeKey = getRangeKey(obj);
if (hashKey != null){
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
if (rangeKey != null)
mapper.load(persistentClass, hashKey.toString(), rangeKey.toString());
else
mapper.load(persistentClass, hashKey.toString());
mapper.save(obj);
}
else
throw new GoraException("Error while retrieving keys from object: " + obj.toString());
}catch(NullPointerException npe){
LOG.error("Error while putting an item. " + npe.toString());
npe.printStackTrace();
}catch(Exception e){
LOG.error("Error while putting an item. " + obj.toString());
e.printStackTrace();
}
}
/**
* Deletes the object using key
* @return true for a successful process
* @throws IOException
*/
@Override
public boolean delete(K key) {
try{
T object = null;
Object rangeKey = null, hashKey = null;
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
for (Method met :key.getClass().getDeclaredMethods()){
if(met.getName().equals("getRangeKey")){
Object [] params = null;
rangeKey = met.invoke(key, params);
break;
}
}
for (Method met :key.getClass().getDeclaredMethods()){
if(met.getName().equals("getHashKey")){
Object [] params = null;
hashKey = met.invoke(key, params);
break;
}
}
if (hashKey == null) object = (T) mapper.load(persistentClass, key);
if (rangeKey == null)
object = (T) mapper.load(persistentClass, hashKey);
else
object = (T) mapper.load(persistentClass, hashKey, rangeKey);
if (object == null) return false;
// setting key for dynamodbMapper
mapper.delete(object);
return true;
}catch(Exception e){
LOG.error("Error while deleting value with key " + key.toString());
LOG.error(e.getMessage());
return false;
}
}
/**
* Deletes items using a specific query
* @throws IOException
*/
@Override
@SuppressWarnings("unchecked")
public long deleteByQuery(Query<K, T> query) {
// TODO verify whether or not we are deleting a whole row
//String[] fields = getFieldsToQuery(query.getFields());
//find whether all fields are queried, which means that complete
//rows will be deleted
//boolean isAllFields = Arrays.equals(fields
// , getBeanFactory().getCachedPersistent().getFields());
Result<K, T> result = execute(query);
ArrayList<T> deletes = new ArrayList<T>();
try {
while(result.next()) {
T resultObj = result.get();
deletes.add(resultObj);
@SuppressWarnings("rawtypes")
DynamoDBKey dKey = new DynamoDBKey();
dKey.setHashKey(getHashKey(resultObj));
dKey.setRangeKey(getRangeKey(resultObj));
delete((K)dKey);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return deletes.size();
}
/**
* Gets a hash key from an object of type T
* @param obj Object from which we will get a hash key
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private Object getHashKey(T obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Object hashKey = null;
for (Method met : obj.getClass().getDeclaredMethods()){
if(met.getName().equals("getHashKey")){
Object [] params = null;
hashKey = met.invoke(obj, params);
break;
}
}
return hashKey;
}
/**
* Gets a hash key from a key of type K
* @param obj Object from which we will get a hash key
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private Object getHashKey(K obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Object hashKey = null;
for (Method met : obj.getClass().getDeclaredMethods()){
if(met.getName().equals("getHashKey")){
Object [] params = null;
hashKey = met.invoke(obj, params);
break;
}
}
return hashKey;
}
/**
* Gets a range key from an object T
* @param obj Object from which a range key will be extracted
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private Object getRangeKey(T obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Object rangeKey = null;
for (Method met : obj.getClass().getDeclaredMethods()){
if(met.getName().equals("getRangeKey")){
Object [] params = null;
rangeKey = met.invoke(obj, params);
break;
}
}
return rangeKey;
}
/**
* Gets a range key from a key obj
* @param obj Object from which a range key will be extracted
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private Object getRangeKey(K obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Object rangeKey = null;
for (Method met : obj.getClass().getDeclaredMethods()){
if(met.getName().equals("getRangeKey")){
Object [] params = null;
rangeKey = met.invoke(obj, params);
break;
}
}
return rangeKey;
}
public List<PartitionQuery<K, T>> getPartitions(Query<K, T> query) throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
/**
* flushes objects to DynamoDB
* @throws IOException
*/
public void flush() {
// TODO Auto-generated method stub
}
public void setBeanFactory(BeanFactory<K, T> beanFactory) {
// TODO Auto-generated method stub
}
public BeanFactory<K, T> getBeanFactory() {
// TODO Auto-generated method stub
return null;
}
@Override
/**
* Closes the data store.
*/
public void close() {
LOG.debug("Datastore closed.");
flush();
}
}