//
// typica - A client library for Amazon Web Services
// Copyright (C) 2007 Xerox 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.xerox.amazonws.sdb;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.xml.bind.JAXBException;
import org.xml.sax.SAXException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.HttpClient;
import org.apache.http.HttpException;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import com.xerox.amazonws.common.AWSException;
import com.xerox.amazonws.common.AWSQueryConnection;
import com.xerox.amazonws.typica.sdb.jaxb.Attribute;
import com.xerox.amazonws.typica.sdb.jaxb.BatchDeleteAttributesResponse;
import com.xerox.amazonws.typica.sdb.jaxb.BatchPutAttributesResponse;
import com.xerox.amazonws.typica.sdb.jaxb.DomainMetadataResponse;
import com.xerox.amazonws.typica.sdb.jaxb.SelectResponse;
/**
* This class provides an interface with the Amazon SDB service. It provides methods for
* listing and deleting items.
*
* @author D. Kavanagh
* @author developer@dotech.com
*/
public class Domain extends AWSQueryConnection {
private static Log logger = LogFactory.getLog(Domain.class);
private String domainName;
private int maxThreads = 30;
private ThreadPoolExecutor executor;
protected Domain(String domainName, String awsAccessId,
String awsSecretKey, boolean isSecure,
String server, int port) throws SDBException {
super(awsAccessId, awsSecretKey, isSecure, server, port);
this.domainName = domainName;
SimpleDB.setVersionHeader(this);
}
/**
* Gets the name of the domain represented by this object.
*
* @return the name of the domain
*/
public String getName() {
return domainName;
}
/**
* Gets the max number of threads to use for the threaded operations.
*
* @return max number of threads being used
*/
public int getMaxThreads() {
return maxThreads;
}
/**
* Sets the max number of threads to use for the threaded operations.
*
* @param threads the new max to set
*/
public void setMaxThreads(int threads) {
maxThreads = threads;
}
/**
* Method for getting an Item object without getting a list of them.
*
* @param identifier id of the item
* @return the object representing the item
* @throws SDBException wraps checked exceptions
*/
public Item getItem(String identifier) throws SDBException {
Item ret = new Item(identifier, domainName, getAwsAccessKeyId(), getSecretAccessKey(),
isSecure(), getPort(), getServer());
ret.setSignatureVersion(getSignatureVersion());
ret.setHttpClient(getHttpClient());
return ret;
}
/**
* Deletes an item.
*
* @param identifier the name of the item to be deleted
* @throws SDBException wraps checked exceptions
*/
public void deleteItem(String identifier) throws SDBException {
getItem(identifier).deleteAttributes(null);
}
/**
* Deletes an item, with conditions
*
* @param identifier the name of the item to be deleted
* @throws SDBException wraps checked exceptions
*/
public void deleteItem(String identifier, List<Condition> conditions) throws SDBException {
getItem(identifier).deleteAttributes(null, conditions);
}
/**
* Returns information about the domain.
*
* @return the object containing metadata about this domain
* @throws SDBException wraps checked exceptions
*/
public DomainMetadataResult getMetadata() throws SDBException {
Map<String, String> params = new HashMap<String, String>();
params.put("DomainName", domainName);
HttpGet method = new HttpGet();
DomainMetadataResponse response =
makeRequestInt(method, "DomainMetadata", params, DomainMetadataResponse.class);
return new DomainMetadataResult(response.getResponseMetadata().getRequestId(),
response.getResponseMetadata().getBoxUsage(),
Integer.parseInt(response.getDomainMetadataResult().getItemCount()),
Integer.parseInt(response.getDomainMetadataResult().getAttributeValueCount()),
Integer.parseInt(response.getDomainMetadataResult().getAttributeNameCount()),
Long.parseLong(response.getDomainMetadataResult().getItemNamesSizeBytes()),
Long.parseLong(response.getDomainMetadataResult().getAttributeValuesSizeBytes()),
Long.parseLong(response.getDomainMetadataResult().getAttributeNamesSizeBytes()),
new Date(Long.parseLong(response.getDomainMetadataResult().getTimestamp())*1000));
}
/**
* This method supports selecting items/attributers based on the select syntax
*
* @param selectExpression the select query
* @param nextToken the next token, for fetching more results from a previous query
* @return an object containing query results and stats
* @throws SDBException wraps checked exceptions
*/
public QueryWithAttributesResult selectItems(String selectExpression, String nextToken) throws SDBException {
return selectItems(selectExpression, nextToken, false);
}
/**
* This method supports selecting items/attributers based on the select syntax
*
* @param selectExpression the select query
* @param nextToken the next token, for fetching more results from a previous query
* @param consistent if true, consistency is assured
* @return an object containing query results and stats
* @throws SDBException wraps checked exceptions
*/
public QueryWithAttributesResult selectItems(String selectExpression, String nextToken, boolean consistent) throws SDBException {
Map<String, String> params = new HashMap<String, String>();
params.put("SelectExpression", selectExpression);
if (nextToken != null) {
params.put("NextToken", nextToken);
}
if (consistent) {
params.put("ConsistentRead", "true");
}
HttpGet method = new HttpGet();
SelectResponse response =
makeRequestInt(method, "Select", params, SelectResponse.class);
Map<String, List<ItemAttribute>> results = new LinkedHashMap<String, List<ItemAttribute>>();
for (com.xerox.amazonws.typica.sdb.jaxb.Item i : response.getSelectResult().getItems()) {
List<ItemAttribute> attrs = new ArrayList<ItemAttribute>();
for (Attribute a : i.getAttributes()) {
attrs.add(createAttribute(a));
}
String iName = i.getName().getValue();
String encoding = i.getName().getEncoding();
if (encoding != null && encoding.equals("base64")) {
iName = new String(Base64.decodeBase64(iName.getBytes()));
}
results.put(iName, attrs);
}
return new QueryWithAttributesResult(
response.getSelectResult().getNextToken(),
response.getResponseMetadata().getRequestId(),
response.getResponseMetadata().getBoxUsage(),
results);
}
/**
* Batch inserts multiple items w/ attributes
*
* @param attributes list of attributes to add
* @throws SDBException wraps checked exceptions
*/
public SDBResult batchPutAttributes(Map<String, List<ItemAttribute>> items) throws SDBException {
Map<String, String> params = new HashMap<String, String>();
params.put("DomainName", domainName);
int k=1;
for (String item : items.keySet()) {
params.put("Item."+k+".ItemName", item);
int i=1;
for (ItemAttribute attr : items.get(item)) {
String val = attr.getValue();
if (val != null) {
params.put("Item."+k+".Attribute."+i+".Name", attr.getName());
params.put("Item."+k+".Attribute."+i+".Value", val);
if (attr.isReplace()) {
params.put("Item."+k+".Attribute."+i+".Replace", "true");
}
i++;
}
}
k++;
}
HttpPost method = new HttpPost();
BatchPutAttributesResponse response =
makeRequestInt(method, "BatchPutAttributes", params, BatchPutAttributesResponse.class);
return new SDBResult(null,
response.getResponseMetadata().getRequestId(),
response.getResponseMetadata().getBoxUsage());
}
static List<Domain> createList(String [] domainNames, String awsAccessKeyId,
String awsSecretAccessKey, boolean isSecure,
String server, int port, int signatureVersion, HttpClient hc)
throws SDBException {
ArrayList<Domain> ret = new ArrayList<Domain>();
for (int i=0; i<domainNames.length; i++) {
Domain dom = new Domain(domainNames[i], awsAccessKeyId, awsSecretAccessKey, isSecure, server, port);
dom.setSignatureVersion(signatureVersion);
dom.setHttpClient(hc);
ret.add(dom);
}
return ret;
}
/**
* Batch deletes multiple items w/ attributes
*
* @param attributes list of attributes to delete
* @throws SDBException wraps checked exceptions
*/
public SDBResult batchDeleteAttributes(Map<String, List<ItemAttribute>> items) throws SDBException {
Map<String, String> params = new HashMap<String, String>();
params.put("DomainName", domainName);
int k=1;
for (String item : items.keySet()) {
params.put("Item."+k+".ItemName", item);
int i=1;
for (ItemAttribute attr : items.get(item)) {
String val = attr.getValue();
if (val != null) {
params.put("Item."+k+".Attribute."+i+".Name", attr.getName());
params.put("Item."+k+".Attribute."+i+".Value", val);
if (attr.isReplace()) {
params.put("Item."+k+".Attribute."+i+".Replace", "true");
}
i++;
}
}
k++;
}
HttpPost method = new HttpPost();
BatchDeleteAttributesResponse response =
makeRequestInt(method, "BatchDeleteAttributes", params, BatchDeleteAttributesResponse.class);
return new SDBResult(null,
response.getResponseMetadata().getRequestId(),
response.getResponseMetadata().getBoxUsage());
}
public ThreadPoolExecutor getThreadPoolExecutor() {
if (executor != null) {
return executor;
}
else {
return new ThreadPoolExecutor(maxThreads, maxThreads, 5,
TimeUnit.SECONDS, new ArrayBlockingQueue(maxThreads));
}
}
public void setThreadPoolExecutor(ThreadPoolExecutor executor) {
this.executor = executor;
}
protected class RejectionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// ok, on the rare occasion, just run it here!
r.run();
}
}
protected <T> T makeRequestInt(HttpRequestBase method, String action, Map<String, String> params, Class<T> respType)
throws SDBException {
try {
return makeRequest(method, action, params, respType);
} catch (AWSException ex) {
throw new SDBException(ex);
} catch (JAXBException ex) {
throw new SDBException("Problem parsing returned message.", ex);
} catch (SAXException ex) {
throw new SDBException("Problem parsing returned message.", ex);
} catch (HttpException ex) {
throw new SDBException(ex.getMessage(), ex);
} catch (IOException ex) {
throw new SDBException(ex.getMessage(), ex);
}
}
private ItemAttribute createAttribute(Attribute a) {
String name = a.getName().getValue();
String encoding = a.getName().getEncoding();
if (encoding != null && encoding.equals("base64")) {
name = new String(Base64.decodeBase64(name.getBytes()));
}
String value = a.getValue().getValue();
encoding = a.getValue().getEncoding();
if (encoding != null && encoding.equals("base64")) {
value = new String(Base64.decodeBase64(value.getBytes()));
}
return new ItemAttribute(name, value, false);
}
}