//
// 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.HashMap;
import java.util.Hashtable;
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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import com.xerox.amazonws.common.AWSQueryConnection;
import com.xerox.amazonws.typica.sdb.jaxb.QueryResponse;
/**
* 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) throws SDBException {
super(awsAccessId, awsSecretKey, isSecure, server, isSecure ? 443 : 80);
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(), getServer());
ret.setSignatureVersion(getSignatureVersion());
ret.setHttpClient(getHttpClient());
return ret;
}
/**
* Gets a list of all items in this domain
*
* @return the object containing the items, a more token, etc.
* @throws SDBException wraps checked exceptions
*/
public QueryResult listItems() throws SDBException {
return listItems(null);
}
/**
* Gets a list of items in this domain filtered by the query string.
*
* @param queryString the filter statement
* @return the object containing the items, a more token, etc.
* @throws SDBException wraps checked exceptions
*/
public QueryResult listItems(String queryString) throws SDBException {
return listItems(queryString, null);
}
/**
* Gets a list of items in this domain filtered by the query string.
*
* @param queryString the filter statement
* @param nextToken the token used to return more items in the query result set
* @return the object containing the items, a more token, etc.
* @throws SDBException wraps checked exceptions
*/
public QueryResult listItems(String queryString, String nextToken) throws SDBException {
return listItems(queryString, null, 0);
}
/**
* Gets a list of items in this domain filtered by the query string.
*
* @param queryString the filter statement
* @param nextToken the token used to return more items in the query result set
* @param maxResults a limit to the number of results to return now
* @return the object containing the items, a more token, etc.
* @throws SDBException wraps checked exceptions
*/
public QueryResult listItems(String queryString, String nextToken, int maxResults) throws SDBException {
Map<String, String> params = new HashMap<String, String>();
params.put("DomainName", domainName);
params.put("QueryExpression", (queryString==null)?"":queryString);
if (nextToken != null) {
params.put("NextToken", nextToken);
}
if (maxResults > 0) {
params.put("MaxNumberOfItems", ""+maxResults);
}
GetMethod method = new GetMethod();
try {
QueryResponse response =
makeRequest(method, "Query", params, QueryResponse.class);
return new QueryResult(response.getQueryResult().getNextToken(),
Item.createList(response.getQueryResult().getItemNames().toArray(new String[] {}), domainName,
getAwsAccessKeyId(), getSecretAccessKey(),
isSecure(), getServer(), getSignatureVersion(), getHttpClient()));
} catch (JAXBException 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);
} finally {
method.releaseConnection();
}
}
/**
* Gets attributes of given items. This method threads off the get requests and
* aggregates the responses.
*
* @param items the list of items to get attributes for
* @return the map of items with lists of attributes
* @throws SDBException wraps checked exceptions
*/
public Map<String, List<ItemAttribute>> getItemsAttributes(List<String> items) throws SDBException {
Map<String, List<ItemAttribute>> results = new Hashtable<String, List<ItemAttribute>>();
ThreadPoolExecutor pool = getThreadPoolExecutor();
pool.setRejectedExecutionHandler(new RejectionHandler());
Counter running = new Counter(0);
for (String item : items) {
while (pool.getActiveCount() == pool.getMaximumPoolSize()) {
try { Thread.sleep(100); } catch (InterruptedException ex) { }
}
synchronized (running) {
running.increment();
}
pool.execute(new AttrWorker(getItem(item), running, results, null));
Thread.yield();
}
while (true) {
if (running.getValue() == 0) {
break;
}
try { Thread.sleep(500); } catch (InterruptedException ex) { }
}
if (this.executor == null) {
pool.shutdown();
}
return results;
}
/**
* Gets attributes of given items. This method threads off the get requests and
* aggregates the responses.
*
* @param items the list of items to get attributes for
* @param listener class that will be notified when items are ready
* @throws SDBException wraps checked exceptions
*/
public void getItemsAttributes(List<String> items, ItemListener listener) throws SDBException {
ThreadPoolExecutor pool = getThreadPoolExecutor();
pool.setRejectedExecutionHandler(new RejectionHandler());
Counter running = new Counter(0);
for (String item : items) {
while (pool.getActiveCount() == pool.getMaximumPoolSize()) {
try { Thread.sleep(100); } catch (InterruptedException ex) { }
}
synchronized (running) {
running.increment();
}
pool.execute(new AttrWorker(getItem(item), running, null, listener));
Thread.yield();
}
while (true) {
if (running.getValue() == 0) {
break;
}
try { Thread.sleep(500); } catch (InterruptedException ex) { }
}
if (this.executor == null) {
pool.shutdown();
}
}
/**
* Gets attributes of items specified in the query string. This method threads off the
* get requests and aggregates the responses.
*
* @param queryString the filter statement
* @param listener class that will be notified when items are ready
* @throws SDBException wraps checked exceptions
*/
public void listItemsAttributes(String queryString, ItemListener listener) throws SDBException {
ThreadPoolExecutor pool = getThreadPoolExecutor();
pool.setRejectedExecutionHandler(new RejectionHandler());
String nextToken = "";
Counter running = new Counter(0);
do {
try {
QueryResult result = listItems(queryString, nextToken, 250);
List<Item> items = result.getItemList();
for (Item i : items) {
while (pool.getActiveCount() == pool.getMaximumPoolSize()) {
try { Thread.sleep(100); } catch (InterruptedException ex) { }
}
synchronized (running) {
running.increment();
}
pool.execute(new AttrWorker(i, running, null, listener));
Thread.yield();
}
nextToken = result.getNextToken();
}
catch (SDBException ex) {
System.out.println("Query '" + queryString + "' Failure: ");
ex.printStackTrace();
}
} while (nextToken != null && nextToken.trim().length() > 0);
while (true) {
if (running.getValue() == 0) {
break;
}
try { Thread.sleep(500); } catch (InterruptedException ex) { }
}
if (this.executor == null) {
pool.shutdown();
}
}
/**
* 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);
}
static List<Domain> createList(String [] domainNames, String awsAccessKeyId,
String awsSecretAccessKey, boolean isSecure,
String server, 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);
dom.setSignatureVersion(signatureVersion);
dom.setHttpClient(hc);
ret.add(dom);
}
return ret;
}
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();
}
}
}