http://code.google.com/p/typica/source/detail?r=322
Added:
/trunk/conf/dtlogo150.png
/trunk/java/com/xerox/amazonws/simpledb
/trunk/java/com/xerox/amazonws/simpledb/Condition.java
/trunk/java/com/xerox/amazonws/simpledb/DataUtils.java
/trunk/java/com/xerox/amazonws/simpledb/Domain.java
/trunk/java/com/xerox/amazonws/simpledb/DomainMetadata.java
/trunk/java/com/xerox/amazonws/simpledb/Item.java
/trunk/java/com/xerox/amazonws/simpledb/ItemCache.java
/trunk/java/com/xerox/amazonws/simpledb/ItemListener.java
/trunk/java/com/xerox/amazonws/simpledb/ItemVO.java
/trunk/java/com/xerox/amazonws/simpledb/SDBException.java
/trunk/java/com/xerox/amazonws/simpledb/SDBListResult.java
/trunk/java/com/xerox/amazonws/simpledb/SDBResult.java
/trunk/java/com/xerox/amazonws/simpledb/SimpleDB.java
/trunk/java/com/xerox/amazonws/simpledb/SimpleItemCache.java
/trunk/test/java/QueryTool.java
Modified:
/trunk/build.xml
/trunk/java/com/xerox/amazonws/common/AWSConnection.java
/trunk/java/com/xerox/amazonws/common/TypicaHttpClient.java
/trunk/test/java/ShowQueueLoadHistory.java
=======================================
--- /dev/null
+++ /trunk/conf/dtlogo150.png Tue Apr 13 07:39:30 2010
Binary file, no diff available.
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/Condition.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,54 @@
+//
+// 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.simpledb;
+
+/**
+ * This class is an immutable representation of an item attribute.
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public class Condition {
+ private String name;
+ private String value;
+ private boolean exists;
+
+ public Condition(String name, String value) {
+ this.name = name;
+ this.value = value;
+ this.exists = false;
+ }
+
+ public Condition(String name, boolean exists) {
+ this.name = name;
+ this.value = null;
+ this.exists = exists;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public boolean isExists() {
+ return exists;
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/DataUtils.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,437 @@
+//
+// 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.
+//
+// NOTE: the original code came from the Amazon SimpleDB java client.
+// The original copywrite notice is included below
+//
+
+/*******************************************************************************
+ * Copyright 2007 Amazon Technologies, Inc.
+ * 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://aws.amazon.com/apache2.0
+ * This file 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.
+ *
*****************************************************************************
+ * __ _ _ ___
+ * ( )( \/\/ )/ __)
+ * /__\ \ / \__ \
+ * (_)(_) \/\/ (___/
+ *
+ * Amazon Simple DB Java Library
+ * API Version: 2007-11-07
+ * Generated: Fri Jan 18 01:13:17 PST 2008
+ *
+ */
+
+package com.xerox.amazonws.simpledb;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Provides collection of static functions for conversion of various
values into strings that may be
+ * compared lexicographically.
+ */
+public class DataUtils {
+
+ /** static value hardcoding date format used for conversation of Date
into String */
+ private static String dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
+
+ /**
+ * Encodes positive integer value into a string by zero-padding number
up to the specified number of digits.
+ *
+ * @param number positive integer to be encoded
+ * @param maxNumDigits maximum number of digits in the largest value
in the data set
+ * @return string representation of the zero-padded integer
+ */
+ public static String encodeZeroPadding(int number, int maxNumDigits) {
+ String integerString = Integer.toString(number);
+ int numZeroes = maxNumDigits - integerString.length();
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
integerString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(integerString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes positive long value into a string by zero-padding number up
to the specified number of digits.
+ *
+ * @param number positive long to be encoded
+ * @param maxNumDigits maximum number of digits in the largest value
in the data set
+ * @return string representation of the zero-padded long
+ */
+ public static String encodeZeroPadding(long number, int maxNumDigits) {
+ String longString = Long.toString(number);
+ int numZeroes = maxNumDigits - longString.length();
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
longString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(longString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes positive float value into a string by zero-padding number
up to the specified number of digits
+ *
+ * @param number positive float value to be encoded
+ * @param maxNumDigits maximum number of digits preceding the decimal
point in the largest value in the data set
+ * @return string representation of the zero-padded float value
+ */
+ public static String encodeZeroPadding(float number, int maxNumDigits)
{
+ String floatString = Float.toString(number);
+ int numBeforeDecimal = floatString.indexOf('.');
+ numBeforeDecimal = (numBeforeDecimal >= 0 ? numBeforeDecimal :
floatString.length());
+ int numZeroes = maxNumDigits - numBeforeDecimal;
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
floatString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(floatString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes positive double value into a string by zero-padding number
up to the specified number of digits
+ *
+ * @param number positive double value to be encoded
+ * @param maxNumDigits maximum number of digits preceding the decimal
point in the largest value in the data set
+ * @return string representation of the zero-padded double value
+ */
+ public static String encodeZeroPadding(double number, int
maxNumDigits) {
+ String doubleString = Double.toString(number);
+ int numBeforeDecimal = doubleString.indexOf('.');
+ numBeforeDecimal = (numBeforeDecimal >= 0 ? numBeforeDecimal :
doubleString.length());
+ int numZeroes = maxNumDigits - numBeforeDecimal;
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
doubleString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(doubleString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Decodes zero-padded positive integer value from the string
representation
+ *
+ * @param value zero-padded string representation of the integer
+ * @return original integer value
+ */
+ public static int decodeZeroPaddingInt(String value) {
+ return Integer.parseInt(value, 10);
+ }
+
+ /**
+ * Decodes zero-padded positive long value from the string
representation
+ *
+ * @param value zero-padded string representation of the long
+ * @return original long value
+ */
+ public static long decodeZeroPaddingLong(String value) {
+ return Long.parseLong(value, 10);
+ }
+
+ /**
+ * Decodes zero-padded positive float value from the string
representation
+ *
+ * @param value zero-padded string representation of the float value
+ * @return original float value
+ */
+ public static float decodeZeroPaddingFloat(String value) {
+ return Float.valueOf(value).floatValue();
+ }
+
+ /**
+ * Decodes zero-padded positive double value from the string
representation
+ *
+ * @param value zero-padded string representation of the double value
+ * @return original double value
+ */
+ public static double decodeZeroPaddingDouble(String value) {
+ return Double.valueOf(value).doubleValue();
+ }
+
+ /**
+ * Encodes real integer value into a string by offsetting and
zero-padding
+ * number up to the specified number of digits. Use this encoding
method if the data
+ * range set includes both positive and negative values.
+ *
+ * @param number integer to be encoded
+ * @param maxNumDigits maximum number of digits in the largest
absolute value in the data set
+ * @param offsetValue offset value, has to be greater than absolute
value of any negative number in the data set.
+ * @return string representation of the integer
+ */
+ public static String encodeRealNumberRange(int number, int
maxNumDigits, int offsetValue) {
+ long offsetNumber = number + offsetValue;
+ String longString = Long.toString(offsetNumber);
+ int numZeroes = maxNumDigits - longString.length();
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
longString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(longString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes real long value into a string by offsetting and zero-padding
+ * number up to the specified number of digits. Use this encoding
method if the data
+ * range set includes both positive and negative values.
+ *
+ * @param number long to be encoded
+ * @param maxNumDigits maximum number of digits in the largest
absolute value in the data set
+ * @param offsetValue offset value, has to be greater than absolute
value of any negative number in the data set.
+ * @return string representation of the long
+ */
+ public static String encodeRealNumberRange(long number, int
maxNumDigits, int offsetValue) {
+ long offsetNumber = number + offsetValue;
+ String longString = Long.toString(offsetNumber);
+ int numZeroes = maxNumDigits - longString.length();
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
longString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(longString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes real float value into a string by offsetting and
zero-padding
+ * number up to the specified number of digits. Use this encoding
method if the data
+ * range set includes both positive and negative values.
+ *
+ * @param number float to be encoded
+ * @param maxDigitsLeft maximum number of digits left of the decimal
point in the largest absolute value in the data set
+ * @param maxDigitsRight maximum number of digits right of the decimal
point in the largest absolute value in the data set, i.e. precision
+ * @param offsetValue offset value, has to be greater than absolute
value of any negative number in the data set.
+ * @return string representation of the integer
+ */
+ public static String encodeRealNumberRange(float number, int
maxDigitsLeft, int maxDigitsRight, int offsetValue) {
+ long shiftMultiplier = (long) Math.pow(10, maxDigitsRight);
+ long shiftedNumber = (long) Math.round(number * shiftMultiplier);
+ long shiftedOffset = offsetValue * shiftMultiplier;
+ long offsetNumber = shiftedNumber + shiftedOffset;
+ String longString = Long.toString(offsetNumber);
+ int numBeforeDecimal = longString.length();
+ int numZeroes = maxDigitsLeft + maxDigitsRight - numBeforeDecimal;
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
longString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(longString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Encodes real double value into a string by offsetting and
zero-padding
+ * number up to the specified number of digits. Use this encoding
method if the data
+ * range set includes both positive and negative values.
+ *
+ * @param number double to be encoded
+ * @param maxDigitsLeft maximum number of digits left of the decimal
point in the largest absolute value in the data set
+ * @param maxDigitsRight maximum number of digits right of the decimal
point in the largest absolute value in the data set, i.e. precision
+ * @param offsetValue offset value, has to be greater than absolute
value of any negative number in the data set.
+ * @return string representation of the integer
+ */
+ public static String encodeRealNumberRange(double number, int
maxDigitsLeft, int maxDigitsRight, long offsetValue) {
+ int shiftMultiplier = (int) Math.pow(10, maxDigitsRight);
+ long shiftedNumber = (long) Math.round(number * shiftMultiplier);
+ long shiftedOffset = offsetValue * shiftMultiplier;
+ long offsetNumber = shiftedNumber + shiftedOffset;
+ String longString = Long.toString(offsetNumber);
+ int numBeforeDecimal = longString.length();
+ int numZeroes = maxDigitsLeft + maxDigitsRight - numBeforeDecimal;
+ StringBuffer strBuffer = new StringBuffer(numZeroes +
longString.length());
+ for (int i = 0; i < numZeroes; i++) {
+ strBuffer.insert(i, '0');
+ }
+ strBuffer.append(longString);
+ return strBuffer.toString();
+ }
+
+ /**
+ * Decodes integer value from the string representation that was
created by
+ * using encodeRealNumberRange(..) function.
+ *
+ * @param value string representation of the integer value
+ * @param offsetValue offset value that was used in the original
encoding
+ * @return original integer value
+ */
+ public static int decodeRealNumberRangeInt(String value, int
offsetValue) {
+ long offsetNumber = Long.parseLong(value, 10);
+ return (int) (offsetNumber - offsetValue);
+ }
+
+ /**
+ * Decodes long value from the string representation that was created
by
+ * using encodeRealNumberRange(..) function.
+ *
+ * @param value string representation of the long value
+ * @param offsetValue offset value that was used in the original
encoding
+ * @return original long value
+ */
+ public static long decodeRealNumberRangeLong(String value, long
offsetValue) {
+ long offsetNumber = Long.parseLong(value, 10);
+ return (long) (offsetNumber - offsetValue);
+ }
+
+ /**
+ * Decodes float value from the string representation that was created
by using encodeRealNumberRange(..) function.
+ *
+ * @param value string representation of the integer value
+ * @param maxDigitsRight maximum number of digits left of the decimal
point in
+ * the largest absolute value in the data set (must be the same as the
one used for encoding).
+ * @param offsetValue offset value that was used in the original
encoding
+ * @return original float value
+ */
+ public static float decodeRealNumberRangeFloat(String value, int
maxDigitsRight, int offsetValue) {
+ long offsetNumber = Long.parseLong(value, 10);
+ long shiftMultiplier = (long) Math.pow(10, maxDigitsRight);
+ double tempVal = (double) (offsetNumber - offsetValue *
shiftMultiplier);
+ return (float) (tempVal / (double) (shiftMultiplier));
+ }
+
+ /**
+ * Decodes double value from the string representation that was
created by using encodeRealNumberRange(..) function.
+ *
+ * @param value string representation of the integer value
+ * @param maxDigitsRight maximum number of digits left of the decimal
point in
+ * the largest absolute value in the data set (must be the same as the
one used for encoding).
+ * @param offsetValue offset value that was used in the original
encoding
+ * @return original double value
+ */
+ public static double decodeRealNumberRangeDouble(String value, int
maxDigitsRight, long offsetValue) {
+ long offsetNumber = Long.parseLong(value, 10);
+ int shiftMultiplier = (int) Math.pow(10, maxDigitsRight);
+ double tempVal = (double) (offsetNumber - offsetValue *
shiftMultiplier);
+ return (double) (tempVal / (double) (shiftMultiplier));
+ }
+
+ /**
+ * Encodes date value into string format that can be compared
lexicographically
+ *
+ * @param date date value to be encoded
+ * @return string representation of the date value
+ */
+ public static String encodeDate(Date date) {
+ SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat);
+ /* Java doesn't handle ISO8601 nicely: need to add ':' manually */
+ String result = dateFormatter.format(date);
+ return result.substring(0, result.length() - 2) + ":" +
result.substring(result.length() - 2);
+ }
+
+ /**
+ * Decodes date value from the string representation created using
encodeDate(..) function.
+ *
+ * @param value string representation of the date value
+ * @return original date value
+ */
+ public static Date decodeDate(String value) throws ParseException {
+ String javaValue = value.substring(0, value.length() - 3) +
value.substring(value.length() - 2);
+ SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat);
+ return dateFormatter.parse(javaValue);
+ }
+
+ // the offset added to negative significands to yield proper collating
order
+ private static final BigDecimal SIGNIFICAND_COLLATOR = BigDecimal.TEN;
+
+ // the offset used on certain exponents to yield proper collating order
+ private static final int EXPONENT_COLLATOR = 999;
+
+ private static final DecimalFormat FULL_DECIMAL_FORMAT = new
DecimalFormat();
+ static { FULL_DECIMAL_FORMAT.applyPattern("0.0000000000000000E000"); }
+
+ private static final DecimalFormat SIGNIFICAND_FORMAT = new
DecimalFormat();
+ static { SIGNIFICAND_FORMAT.applyPattern("0.0000000000000000"); }
+
+ /* A Java implementation of Doug Wood's work in progress
+ * "Directory string representation for floating point values"
+ * http://tools.ietf.org/html/draft-wood-ldapext-float-00
+ *
+ * Note: Section 3.5 of the above draft memo should be corrected to read:
+ * 3.5 Negative mantissa and positive exponent (case 1)
+ * When the exponent is positive and the mantissa are negative, the
collating
+ * sequence is flipped for both of them. This is achieved by subtracting
+ * the exponent from 999, and adding the mantissa to 10.
+ *
+ * Note: the term 'significand' is used here rather than 'mantissa'.
+ * Infinity and NaN are not handled.
+ */
+ public static String encodeDouble(double d) {
+ // todo: replace String manipulation with math
+ String decimalString = FULL_DECIMAL_FORMAT.format(d);
+ int splitPoint = decimalString.indexOf('E');
+ String significand = decimalString.substring(0, splitPoint);
+ String exponent = decimalString.substring(splitPoint + 1);
+ boolean negativeExponent = exponent.startsWith("-");
+ String result;
+
+ if (significand.startsWith("-")) {
+ // BigDecimal here preserves significand's last digit during add()
+ BigDecimal significandValue = new BigDecimal(significand);
+ BigDecimal collatedSignificand =
significandValue.add(SIGNIFICAND_COLLATOR);
+ String formattedSignificand =
SIGNIFICAND_FORMAT.format(collatedSignificand);
+
+ if (!negativeExponent) {
+ int exponentValue = EXPONENT_COLLATOR - Integer.parseInt(exponent);
+ result = "1 " + exponentValue + " "+ formattedSignificand;
+ } else {
+ result = "2 " + exponent.substring(1) + " " + formattedSignificand;
+ }
+ } else {
+ if (d == 0.0D) {
+ result = "3 000 0.0000000000000000";
+ } else if (negativeExponent) {
+ int exponentValue = Integer.parseInt(exponent) + EXPONENT_COLLATOR;
+ result = "4 " + exponentValue + " " + significand;
+ } else {
+ result = "5 " + exponent + " " + significand;
+ }
+ }
+ return result;
+ }
+
+ public static double decodeDouble(String s) {
+ char caseNumber = s.charAt(0);
+ if (caseNumber == '3') { return 0.0D; }
+ String exponentString = s.substring(2, 5);
+ int exponent = Integer.parseInt(exponentString);
+ String significand = s.substring(6);
+
+ if (caseNumber == '4') {
+ exponent -= EXPONENT_COLLATOR;
+ } else if (caseNumber == '1' || caseNumber == '2'){
+ BigDecimal collatedSignificand = new BigDecimal(significand);
+ BigDecimal significandValue =
collatedSignificand.subtract(SIGNIFICAND_COLLATOR);
+ significand = significandValue.toString();
+ if (caseNumber == '1') {
+ exponent = EXPONENT_COLLATOR - exponent;
+ } else if (caseNumber == '2'){
+ exponent = -exponent;
+ }
+ }
+ return Double.parseDouble(significand + "E" + exponent);
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/Domain.java Tue Apr 13 07:39:30
2010
@@ -0,0 +1,495 @@
+//
+// 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.simpledb;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.bind.JAXBException;
+
+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 com.xerox.amazonws.common.AWSException;
+import com.xerox.amazonws.common.AWSQueryConnection;
+import com.xerox.amazonws.common.ListResult;
+import com.xerox.amazonws.common.Result;
+import com.xerox.amazonws.typica.sdb.jaxb.Attribute;
+import com.xerox.amazonws.typica.sdb.jaxb.BatchPutAttributesResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.DeleteAttributesResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.DomainMetadataResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.GetAttributesResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.PutAttributesResponse;
+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 deve...@dotech.com
+ */
+public class Domain {
+
+ private static Log logger = LogFactory.getLog(Domain.class);
+
+ private AWSQueryConnection connection; // connection delegate
+
+ private String domainName;
+ private ItemCache cache;
+
+ protected Domain(String domainName, AWSQueryConnection connection)
throws SDBException {
+ this.domainName = domainName;
+ this.connection = connection;
+ }
+
+ /**
+ * Returns connection object, so connection params can be tweaked
+ */
+ public AWSQueryConnection getConnectionDelegate() {
+ return connection;
+ }
+
+ /**
+ * Gets the name of the domain represented by this object.
+ *
+ * @return the name of the domain
+ */
+ public String getName() {
+ return domainName;
+ }
+
+ /**
+ * Returns information about the domain.
+ *
+ * @return the object containing metadata about this domain
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult<DomainMetadata> 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);
+ com.xerox.amazonws.typica.sdb.jaxb.DomainMetadataResult result =
+ response.getDomainMetadataResult();
+ return new
SDBResult<DomainMetadata>(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage(),
+ new DomainMetadata(Integer.parseInt(result.getItemCount()),
+ Integer.parseInt(result.getAttributeValueCount()),
+ Integer.parseInt(result.getAttributeNameCount()),
+ Long.parseLong(result.getItemNamesSizeBytes()),
+ Long.parseLong(result.getAttributeValuesSizeBytes()),
+ Long.parseLong(result.getAttributeNamesSizeBytes()),
+ new Date(Long.parseLong(result.getTimestamp())*1000)));
+ }
+
+ /**
+ * Adds an item.
+ *
+ * @param item the item to add to this domain
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult addItem(Item item) throws SDBException {
+ return addItem(item.getIdentifier(), item.getAttributes(), null);
+ }
+
+ /**
+ * Adds an item. This method also works to add attributes to an existing
item.
+ *
+ * @param identifier the name of the item to be added
+ * @param attributes the attributes to associate with this item
+ * @param conditions the conditions under which attributes should be put
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult addItem(String identifier, Map<String, Set<String>>
attributes, List<Condition> conditions) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ params.put("ItemName", identifier);
+ int i=1;
+ for (String key : attributes.keySet()) {
+ Set<String> vals = attributes.get(key);
+ if (vals != null && vals.size() > 0) {
+ Iterator<String> iter = vals.iterator();
+ while (iter.hasNext()) {
+ String val = iter.next();
+ params.put("Attribute."+i+".Name", key);
+ params.put("Attribute."+i+".Value", val);
+ i++;
+ }
+ }
+ }
+ if (conditions != null) {
+ i=1;
+ for (Condition cond : conditions) {
+ params.put("Expected."+i+".Name", cond.getName());
+ String value = cond.getValue();
+ if (value != null) {
+ params.put("Expected."+i+".Value", value);
+ }
+ else {
+ params.put("Expected."+i+".Exists", cond.isExists()?"true":"false");
+ }
+ i++;
+ }
+ }
+ HttpGet method = new HttpGet();
+ PutAttributesResponse response =
+ makeRequestInt(method, "PutAttributes", params,
PutAttributesResponse.class);
+ if (cache != null) {
+ // create new item object
+ Item newItem = new ItemVO(identifier);
+ Map<String, Set<String>> attrs = newItem.getAttributes();
+ // throw attrs into it
+ attrs.putAll(attributes);
+ Item old = cache.getItem(identifier);
+ if (old != null) {
+ // merge cached attrs with those just set
+ for (String key: old.getAttributes().keySet()) {
+ Set<String> oldAttrs = old.getAttributes().get(key);
+ Set<String> newAttrs = attrs.get(key);
+ if (newAttrs != null) {
+ newAttrs.addAll(oldAttrs);
+ }
+ else {
+ attrs.put(key, oldAttrs);
+ }
+ }
+ }
+ // place/replace item in cache
+ cache.putItem(newItem);
+ }
+ return new SDBResult(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Batch inserts multiple items w/ attributes
+ *
+ * @param attributes list of attributes to add
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult batchPutAttributes(List<Item> items) throws SDBException
{
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ int k=1;
+ for (Item item : items) {
+ params.put("Item."+k+".ItemName", item.getIdentifier());
+ int i=1;
+ for (String attr : item.getAttributes().keySet()) {
+ Set<String> vals = item.getAttributeValues(attr);
+ if (vals != null && vals.size() > 0) {
+ for (String val : vals) {
+ params.put("Item."+k+".Attribute."+i+".Name", attr);
+ params.put("Item."+k+".Attribute."+i+".Value", val);
+ i++;
+// if (attr.isReplace()) {
+// params.put("Item."+k+".Attribute."+i+".Replace", "true");
+// }
+ }
+ }
+ }
+ k++;
+ }
+ HttpGet method = new HttpGet();
+ BatchPutAttributesResponse response =
+ makeRequestInt(method, "BatchPutAttributes", params,
BatchPutAttributesResponse.class);
+ return new SDBResult(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Replace attributes on an item. Using this call will force attribute
values to be
+ * with the new ones supplied.
+ *
+ * @param identifier the name of the item to be added
+ * @param attributes the attributes to associate with this item
+ * @param conditions the conditions under which attributes should be put
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult replaceAttributes(String identifier, Map<String,
Set<String>> attributes, List<Condition> conditions) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ params.put("ItemName", identifier);
+ int i=1;
+ for (String key : attributes.keySet()) {
+ Set<String> vals = attributes.get(key);
+ if (vals != null && vals.size() > 0) {
+ Iterator<String> iter = vals.iterator();
+ while (iter.hasNext()) {
+ String val = iter.next();
+ params.put("Attribute."+i+".Name", key);
+ params.put("Attribute."+i+".Value", val);
+ params.put("Attribute."+i+".Replace", "true");
+ i++;
+ }
+ }
+ }
+ if (conditions != null) {
+ i=1;
+ for (Condition cond : conditions) {
+ params.put("Expected."+i+".Name", cond.getName());
+ String value = cond.getValue();
+ if (value != null) {
+ params.put("Expected."+i+".Value", value);
+ }
+ else {
+ params.put("Expected."+i+".Exists", cond.isExists()?"true":"false");
+ }
+ i++;
+ }
+ }
+ HttpGet method = new HttpGet();
+ PutAttributesResponse response =
+ makeRequestInt(method, "PutAttributes", params,
PutAttributesResponse.class);
+ if (cache != null) {
+ // create new item object
+ Item newItem = new ItemVO(identifier);
+ Map<String, Set<String>> attrs = newItem.getAttributes();
+ // throw attrs into it
+ attrs.putAll(attributes);
+ Item old = cache.getItem(identifier);
+ if (old != null) {
+ // merge cached attrs
+ attrs.putAll(old.getAttributes());
+ }
+ // place/replace item in cache
+ cache.putItem(newItem);
+ }
+ return new SDBResult(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Deletes attributes from an item
+ *
+ * @param identifier the name of the item
+ * @param attributes the names of the attributes to be deleted
+ * @param conditions the conditions under which attributes should be put
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult deleteAttributes(String identifier, Set<String>
attributes, List<Condition> conditions) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ params.put("ItemName", identifier);
+ int i=1;
+ if (attributes != null && attributes.size() > 0) {
+ Iterator<String> iter = attributes.iterator();
+ while (iter.hasNext()) {
+ String val = iter.next();
+ params.put("Attribute."+i+".Name", val);
+ i++;
+ }
+ }
+ if (conditions != null) {
+ i=1;
+ for (Condition cond : conditions) {
+ params.put("Expected."+i+".Name", cond.getName());
+ String value = cond.getValue();
+ if (value != null) {
+ params.put("Expected."+i+".Value", value);
+ }
+ else {
+ params.put("Expected."+i+".Exists", cond.isExists()?"true":"false");
+ }
+ i++;
+ }
+ }
+ HttpGet method = new HttpGet();
+ DeleteAttributesResponse response =
+ makeRequestInt(method, "DeleteAttributes", params,
DeleteAttributesResponse.class);
+ if (cache != null) {
+ // cache.removeItem(identifier);
+ }
+ return new SDBResult(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Deletes an item.
+ *
+ * @param identifier the name of the item to be deleted
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult deleteItem(String identifier) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ params.put("ItemName", identifier);
+ HttpGet method = new HttpGet();
+ DeleteAttributesResponse response =
+ makeRequestInt(method, "DeleteAttributes", params,
DeleteAttributesResponse.class);
+ if (cache != null) {
+ cache.removeItem(identifier);
+ }
+ return new SDBResult(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Returns an named item.
+ *
+ * @param identifier the name of the item to be deleted
+ * @throws SDBException wraps checked exceptions
+ */
+ public Result<Item> getItem(String identifier) throws SDBException {
+ if (cache != null) {
+ Item cached = cache.getItem(identifier);
+ if (cached != null) {
+ return new Result<Item>(null, cached);
+ }
+ // else, go fetch it anyway
+ }
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", domainName);
+ params.put("ItemName", identifier);
+ HttpGet method = new HttpGet();
+ GetAttributesResponse response =
+ makeRequestInt(method, "GetAttributes", params,
GetAttributesResponse.class);
+ Item newItem = new ItemVO(identifier);
+ Map<String, Set<String>> attrs = newItem.getAttributes();
+ for (Attribute a : response.getGetAttributesResult().getAttributes()) {
+ 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()));
+ }
+ Set<String> vals = attrs.get(name);
+ if (vals == null) {
+ vals = Collections.synchronizedSet(new HashSet<String>());
+ attrs.put(name, vals);
+ }
+ vals.add(value);
+ }
+ if (cache != null) {
+ cache.putItem(newItem);
+ }
+ return new Result<Item>(null, newItem);
+ }
+
+ /**
+ * Performs a query against this domain. A set of items is returned which
may not be
+ * complete. If the nextToken in the result is set, this method can be
called again
+ * with the same query and the supplied nextToken.
+ *
+ * @param selectExpression the query to be run
+ * @param nextToken an optional token to get more results
+ * @param consistent if true, consistency is assured
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBListResult<Item> 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);
+ SDBListResult<Item> ret = new SDBListResult<Item>(
+ response.getSelectResult().getNextToken(),
+ response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ List<Item> results = ret.getItems();
+ for (com.xerox.amazonws.typica.sdb.jaxb.Item i :
response.getSelectResult().getItems()) {
+ String iName = i.getName().getValue();
+ String encoding = i.getName().getEncoding();
+ if (encoding != null && encoding.equals("base64")) {
+ iName = new String(Base64.decodeBase64(iName.getBytes()));
+ }
+ Item newItem = new ItemVO(iName);
+ Map<String, Set<String>> attrs = newItem.getAttributes();
+ for (Attribute a : i.getAttributes()) {
+ String name = a.getName().getValue();
+ 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()));
+ }
+ Set<String> vals = attrs.get(name);
+ if (vals == null) {
+ vals = Collections.synchronizedSet(new HashSet<String>());
+ attrs.put(name, vals);
+ }
+ vals.add(value);
+ }
+ results.add(newItem);
+ }
+
+ return ret;
+ }
+
+ public ItemCache getItemCache() {
+ return this.cache;
+ }
+
+ public void setCacheProvider(ItemCache cache) {
+ this.cache = cache;
+ }
+
+ static List<Domain> createList(String [] domainNames, AWSQueryConnection
connection)
+ throws SDBException {
+ ArrayList<Domain> ret = new ArrayList<Domain>();
+ for (int i=0; i<domainNames.length; i++) {
+ Domain dom = new Domain(domainNames[i], connection);
+ ret.add(dom);
+ }
+ return ret;
+ }
+
+ protected <T> T makeRequestInt(HttpRequestBase method, String action,
Map<String, String> params, Class<T> respType)
+ throws SDBException {
+ try {
+ return connection.makeRequest(method, action, params, respType);
+ } catch (AWSException ex) {
+ throw new SDBException(ex);
+ } 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);
+ }
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/DomainMetadata.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,72 @@
+//
+// 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.simpledb;
+
+import java.util.Date;
+import java.util.List;
+
+public class DomainMetadata {
+ private int itemCount;
+ private int attributeValueCount;
+ private int attributeNameCount;
+ private long itemNamesSizeBytes;
+ private long attributeValuesSizeBytes;
+ private long attributeNamesSizeBytes;
+ private Date timestamp;
+
+ DomainMetadata(int itemCount, int attributeValueCount, int
attributeNameCount,
+ long itemNamesSizeBytes, long attributeValuesSizeBytes, long
attributeNamesSizeBytes,
+ Date timestamp) {
+ this.itemCount = itemCount;
+ this.attributeValueCount = attributeValueCount;
+ this.attributeNameCount = attributeNameCount;
+ this.itemNamesSizeBytes = itemNamesSizeBytes;
+ this.attributeValuesSizeBytes = attributeValuesSizeBytes;
+ this.attributeNamesSizeBytes = attributeNamesSizeBytes;
+ this.timestamp = timestamp;
+ }
+
+ public int getItemCount() {
+ return itemCount;
+ }
+
+ public int getAttributeValueCount() {
+ return attributeValueCount;
+ }
+
+ public int getAttributeNameCount() {
+ return attributeNameCount;
+ }
+
+ public long getItemNamesSizeBytes() {
+ return itemNamesSizeBytes;
+ }
+
+ public long getAttributeValuesSizeBytes() {
+ return attributeValuesSizeBytes;
+ }
+
+ public long getAttributeNamesSizeBytes() {
+ return attributeNamesSizeBytes;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/Item.java Tue Apr 13 07:39:30
2010
@@ -0,0 +1,61 @@
+//
+// 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.simpledb;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class represents an item in SimpleDB. To modify this item,
+ * use the interfaces in Domain.
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public interface Item {
+
+ /**
+ * Gets the name of the identifier that is unique to this Item
+ *
+ * @return the id
+ */
+ public String getIdentifier();
+
+ /**
+ * Gets a map of all attributes for this item
+ *
+ * @return the map of attributes
+ */
+ public Map<String, Set<String>> getAttributes();
+
+ /**
+ * Gets a single attribute value. If multiple values exist, only the first
+ * will be returned. Calling getAttributeValues(String) should be used
instead.
+ *
+ * @return the attribute value
+ */
+ public String getAttribute(String name);
+
+ /**
+ * Gets a Set of values for the named attribute.
+ *
+ * @return the set of attribute values
+ */
+ public Set<String> getAttributeValues(String name);
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/ItemCache.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,43 @@
+package com.xerox.amazonws.simpledb;
+
+import java.util.List;
+
+/**
+ * This interface describes calls that the Domain will make into a caching
+ * system. For now, only items are cached by id.
+ */
+public interface ItemCache {
+
+ /**
+ * This retrieves an item from the cache. A null is returned if the item
+ * is not cachced.
+ *
+ * @param id the identifier for the item being retrieved
+ * @return the item found (or null)
+ */
+ public Item getItem(String id);
+
+ /**
+ * Stores an item in the cache.
+ *
+ * @param i the item to be stored
+ */
+ public void putItem(Item i);
+
+ /**
+ * Removes an item from the cache.
+ *
+ * @param id the identifier for the item being removed
+ */
+ public void removeItem(String id);
+
+ /**
+ * Retrieves a complete list of items in the cache
+ */
+ public List<Item> itemSet();
+
+ /**
+ * Clears the cache. This would be used to ensure only new data is being
fetched.
+ */
+ public void clear();
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/ItemListener.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,29 @@
+//
+// 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.simpledb;
+
+/**
+ * This interface describes a class that is called when items are made
available from
+ * a threaded fetch operation (@see
com.xerox.amazonws.sdb.Domain#selectItems)
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public interface ItemListener {
+ public void itemAvailable(Item item);
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/ItemVO.java Tue Apr 13 07:39:30
2010
@@ -0,0 +1,92 @@
+//
+// 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.simpledb;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class represents an item in SimpleDB. To modify this item, use the
interfaces in Domain.
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public class ItemVO implements Item {
+ private static Log logger = LogFactory.getLog(Item.class);
+
+ private String identifier;
+ private Map<String, Set<String>> attributes;
+
+ public ItemVO(String identifier) {
+ this.identifier = identifier;
+ attributes = Collections.synchronizedMap(new HashMap<String,
Set<String>>());
+ }
+
+ /**
+ * Gets the name of the identifier that is unique to this Item
+ *
+ * @return the id
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Gets a map of all attributes for this item
+ *
+ * @return the map of attributes
+ */
+ public Map<String, Set<String>> getAttributes() {
+ return this.attributes;
+ }
+
+ /**
+ * Gets a single attribute value. If multiple values exist, only the first
+ * will be returned. Calling getAttributeValues(String) should be used
instead.
+ *
+ * @return the attribute value
+ */
+ public String getAttribute(String name) {
+ Set vals = this.attributes.get(name);
+ if (vals == null || vals.size() == 0) {
+ return null;
+ }
+ else {
+ return ((Iterator<String>)vals.iterator()).next();
+ }
+ }
+
+ /**
+ * Gets a Set of values for the named attribute.
+ *
+ * @return the set of attribute values
+ */
+ public Set<String> getAttributeValues(String name) {
+ return this.attributes.get(name);
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/SDBException.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,40 @@
+//
+// 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.simpledb;
+
+import com.xerox.amazonws.common.AWSException;
+
+/**
+ * A wrapper exception to simplify catching errors related to sdb activity.
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public class SDBException extends AWSException {
+ public SDBException(String s) {
+ super(s);
+ }
+
+ public SDBException(String s, Exception ex) {
+ super(s, ex);
+ }
+
+ public SDBException(AWSException ex) {
+ super(ex);
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/SDBListResult.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,37 @@
+//
+// 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.simpledb;
+
+import com.xerox.amazonws.common.ListResult;
+
+public class SDBListResult<E> extends ListResult<E> {
+ private String boxUsage;
+
+ SDBListResult(String nextToken, String requestId) {
+ super(nextToken, requestId);
+ }
+
+ SDBListResult(String nextToken, String requestId, String boxUsage) {
+ super(nextToken, requestId);
+ this.boxUsage = boxUsage;
+ }
+
+ public String getBoxUsage() {
+ return boxUsage;
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/SDBResult.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,42 @@
+//
+// 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.simpledb;
+
+import com.xerox.amazonws.common.Result;
+
+public class SDBResult<E> extends Result<E> {
+ private String boxUsage;
+
+ SDBResult(String requestId) {
+ super(requestId);
+ }
+
+ SDBResult(String requestId, String boxUsage) {
+ super(requestId);
+ this.boxUsage = boxUsage;
+ }
+
+ SDBResult(String requestId, String boxUsage, E result) {
+ super(requestId, result);
+ this.boxUsage = boxUsage;
+ }
+
+ public String getBoxUsage() {
+ return boxUsage;
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/SimpleDB.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,247 @@
+//
+// 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.simpledb;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBException;
+
+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 com.xerox.amazonws.common.AWSException;
+import com.xerox.amazonws.common.AWSQueryConnection;
+import com.xerox.amazonws.typica.sdb.jaxb.CreateDomainResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.DeleteDomainResponse;
+import com.xerox.amazonws.typica.sdb.jaxb.ListDomainsResponse;
+
+/**
+ * This class provides an interface with the Amazon SDB service. It
provides high level
+ * methods for listing and creating and deleting domains.
+ *
+ * @author D. Kavanagh
+ * @author deve...@dotech.com
+ */
+public class SimpleDB {
+
+ private static Log logger = LogFactory.getLog(SimpleDB.class);
+
+ private AWSQueryConnection connection; // connection delegate
+
+ /**
+ * Initializes the sdb service with your AWS login information.
+ *
+ * @param awsAccessId The your user key into AWS
+ * @param awsSecretKey The secret string used to generate signatures
for authentication.
+ */
+ public SimpleDB(String awsAccessId, String awsSecretKey) {
+ this(awsAccessId, awsSecretKey, true);
+ }
+
+ /**
+ * Initializes the sdb service with your AWS login information.
+ *
+ * @param awsAccessId The your user key into AWS
+ * @param awsSecretKey The secret string used to generate signatures
for authentication.
+ * @param isSecure True if the data should be encrypted on the wire on
the way to or from SDB.
+ */
+ public SimpleDB(String awsAccessId, String awsSecretKey, boolean
isSecure) {
+ this(awsAccessId, awsSecretKey, isSecure, "sdb.amazonaws.com");
+ }
+
+ /**
+ * Initializes the sdb service with your AWS login information.
+ *
+ * @param awsAccessId The your user key into AWS
+ * @param awsSecretKey The secret string used to generate signatures
for authentication.
+ * @param isSecure True if the data should be encrypted on the wire on
the way to or from SDB.
+ * @param server Which host to connect to. Usually, this will be
sdb.amazonaws.com
+ */
+ public SimpleDB(String awsAccessId, String awsSecretKey, boolean
isSecure,
+ String server)
+ {
+ this(awsAccessId, awsSecretKey, isSecure, server,
+ isSecure ? 443 : 80);
+ }
+
+ /**
+ * Initializes the sdb service with your AWS login information.
+ *
+ * @param awsAccessId The your user key into AWS
+ * @param awsSecretKey The secret string used to generate signatures
for authentication.
+ * @param isSecure True if the data should be encrypted on the wire on
the way to or from SDB.
+ * @param server Which host to connect to. Usually, this will be
sdb.amazonaws.com
+ * @param port Which port to use.
+ */
+ public SimpleDB(String awsAccessId, String awsSecretKey, boolean
isSecure,
+ String server, int port)
+ {
+ connection = new AWSQueryConnection(awsAccessId, awsSecretKey, isSecure,
server, port);
+ setVersionHeader(connection);
+ }
+
+ /**
+ * Returns connection object, so connection params can be tweaked
+ */
+ public AWSQueryConnection getConnectionDelegate() {
+ return connection;
+ }
+
+ /**
+ * This method sets the endpoint (based on EU or US endpoint)
+ *
+ * @param endpointHost the name of the enpoint server
+ */
+ public void setEndpoint(String endpointHost) {
+ connection.setServer(endpointHost);
+ }
+
+ /**
+ * Creates a domain. If domain already exists, no error is thrown.
+ *
+ * @param name name of the new domain
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult<Domain> createDomain(String name) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", name);
+ HttpGet method = new HttpGet();
+ CreateDomainResponse response =
+ makeRequestInt(method, "CreateDomain", params,
CreateDomainResponse.class);
+ Domain ret = new Domain(name, connection);
+ return new
SDBResult<Domain>(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage(), ret);
+ }
+
+ /**
+ * Deletes a domain.
+ *
+ * @param domain the domain to be deleted
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult deleteDomain(Domain domain) throws SDBException {
+ return deleteDomain(domain.getName());
+ }
+
+ /**
+ * Deletes a domain.
+ *
+ * @param name the name of the domain to be deleted
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBResult deleteDomain(String name) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("DomainName", name);
+ HttpGet method = new HttpGet();
+ DeleteDomainResponse response =
+ makeRequestInt(method, "DeleteDomain", params,
DeleteDomainResponse.class);
+ return new
SDBResult<Domain>(response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ }
+
+ /**
+ * Method for getting a Domain object without getting a list of them.
+ *
+ * @param domainName the name of the domain to be returned
+ * @throws SDBException wraps checked exceptions
+ */
+ public Domain getDomain(String domainName) throws SDBException {
+ return new Domain(domainName, connection);
+ }
+
+ /**
+ * Gets a list of domains
+ *
+ * @return A list of {@link Domain} instances.
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBListResult<Domain> listDomains() throws SDBException {
+ return this.listDomains(null);
+ }
+
+ /**
+ * Gets a list of domains
+ *
+ * @param nextToken token to use when retrieving next results
+ * @return A list of {@link Domain} instances.
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBListResult<Domain> listDomains(String nextToken) throws
SDBException {
+ return this.listDomains(nextToken, 0);
+ }
+
+ /**
+ * Gets a list of domains
+ *
+ * @param nextToken token to use when retrieving next results
+ * @param maxResults the max number of results to return (0 means no max
defined)
+ * @throws SDBException wraps checked exceptions
+ */
+ public SDBListResult<Domain> listDomains(String nextToken, int
maxResults) throws SDBException {
+ Map<String, String> params = new HashMap<String, String>();
+ if (nextToken != null) {
+ params.put("NextToken", nextToken);
+ }
+ if (maxResults > 0) {
+ params.put("MaxNumberOfDomains", ""+maxResults);
+ }
+ HttpGet method = new HttpGet();
+ ListDomainsResponse response =
+ makeRequestInt(method, "ListDomains", params,
ListDomainsResponse.class);
+
+ SDBListResult<Domain> ret = new SDBListResult<Domain>(
+ response.getListDomainsResult().getNextToken(),
+ response.getResponseMetadata().getRequestId(),
+ response.getResponseMetadata().getBoxUsage());
+ List<Domain> results = ret.getItems();
+
results.addAll(Domain.createList(response.getListDomainsResult().getDomainNames().toArray(new
String[] {}),
+ connection));
+ return ret;
+ }
+
+ protected <T> T makeRequestInt(HttpRequestBase method, String action,
Map<String, String> params, Class<T> respType)
+ throws SDBException {
+ try {
+ return connection.makeRequest(method, action, params, respType);
+ } catch (AWSException ex) {
+ throw new SDBException(ex);
+ } 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);
+ }
+ }
+
+ static void setVersionHeader(AWSQueryConnection connection) {
+ ArrayList vals = new ArrayList();
+ vals.add("2009-04-15");
+ connection.getHeaders().put("Version", vals);
+ }
+}
=======================================
--- /dev/null
+++ /trunk/java/com/xerox/amazonws/simpledb/SimpleItemCache.java Tue Apr 13
07:39:30 2010
@@ -0,0 +1,57 @@
+package com.xerox.amazonws.simpledb;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Hashtable;
+
+/**
+ * This interface describes calls that the Domain will make into a caching
+ * system. For now, only items are cached by id.
+ */
+public class SimpleItemCache implements ItemCache {
+ // this is thread safe
+ private Hashtable<String, Item> _cache = new Hashtable<String, Item>();
+
+ /**
+ * This retrieves an item from the cache. A null is returned if the item
+ * is not cachced.
+ *
+ * @param id the identifier for the item being retrieved
+ * @return the item found (or null)
+ */
+ public Item getItem(String id) {
+ return _cache.get(id);
+ }
+
+ /**
+ * Stores an item in the cache.
+ *
+ * @param i the item to be stored
+ */
+ public void putItem(Item i) {
+ _cache.put(i.getIdentifier(), i);
+ }
+
+ /**
+ * Removes an item from the cache.
+ *
+ * @param id the identifier for the item being removed
+ */
+ public void removeItem(String id) {
+ _cache.remove(id);
+ }
+
+ /**
+ * Retrieves a complete list of items in the cache
+ */
+ public List<Item> itemSet() {
+ return new ArrayList<Item>(_cache.values());
+ }
+
+ /**
+ * Clears the cache. This would be used to ensure only new data is being
fetched.
+ */
+ public void clear() {
+ _cache.clear();
+ }
+}
=======================================
--- /dev/null
+++ /trunk/test/java/QueryTool.java Tue Apr 13 07:39:30 2010
@@ -0,0 +1,674 @@
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDesktopPane;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.text.BadLocationException;
+
+import com.xerox.amazonws.simpledb.Domain;
+import com.xerox.amazonws.simpledb.DomainMetadata;
+import com.xerox.amazonws.simpledb.Item;
+import com.xerox.amazonws.simpledb.ItemVO;
+import com.xerox.amazonws.simpledb.SDBException;
+import com.xerox.amazonws.simpledb.SDBListResult;
+import com.xerox.amazonws.simpledb.SDBResult;
+import com.xerox.amazonws.simpledb.SimpleDB;
+
+public class QueryTool extends JPanel implements ActionListener {
+ private JFrame parent;
+ private JComboBox regionList;
+ private JComboBox domainList;
+ private JTextArea querySpace;
+ private JDesktopPane results;
+ private SimpleDB sdb;
+ private Domain dom;
+
+ public QueryTool(JFrame parent, String accessId, String secretKey, String
proxyHost, int proxyPort) {
+ this.parent = parent;
+ sdb = new SimpleDB(accessId, secretKey);
+ if (proxyHost != null) {
+ sdb.getConnectionDelegate().setProxyValues(proxyHost, proxyPort);
+ }
+ layoutGUI();
+ loadPrefs();
+ }
+
+ public void shutdown() {
+ savePrefs();
+ }
+
+ public void setDomain(String name) {
+ try {
+ dom = sdb.getDomain(name);
+ } catch (SDBException ex) {
+ System.err.println("Could not create domain object: "+ex.getMessage());
+ System.exit(-1);
+ }
+ }
+
+ private void layoutGUI() {
+ setLayout(new GridBagLayout());
+
+ JButton runQuery = new JButton("run");
+ runQuery.addActionListener(this);
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ add(runQuery, gbc);
+
+ JButton sponsor = new JButton("Sponsored by:", new
ImageIcon("build/classes/dtlogo150.png"));
+ sponsor.setHorizontalTextPosition(SwingConstants.LEFT);
+ sponsor.setBorderPainted(false);
+ sponsor.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ JOptionPane.showInternalMessageDialog(results,
+ "<html><center>Work on QueryTool has been generously sponsored by
directThought.<br>Please consider using them for your next project.<br>See
<a href=\"http://directThought.com/\">directThought.com</a> for more
information.</center></html>",
+ dom.getName()+" metadata",
+ JOptionPane.PLAIN_MESSAGE);
+ }
+ });
+ gbc = new GridBagConstraints();
+ add(sponsor, gbc);
+
+ JLabel regLab = new JLabel("Region:");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.EAST;
+ gbc.weightx = 1.0;
+ add(regLab, gbc);
+
+ regionList = new JComboBox(new String [] {"US", "EU"});
+ regionList.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ String endpoint = "sdb.amazonaws.com";
+ if (regionList.getSelectedItem().equals("EU")) {
+ endpoint = "sdb.eu-west-1.amazonaws.com";
+ }
+ sdb.setEndpoint(endpoint);
+ populateDomainList();
+ setDomain((String)domainList.getSelectedItem());
+ }
+ });
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.EAST;
+ add(regionList, gbc);
+
+ JLabel domLab = new JLabel("Domain:");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.EAST;
+ gbc.weightx = 1.0;
+ add(domLab, gbc);
+
+ domainList = new JComboBox();
+ domainList.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ setDomain((String)domainList.getSelectedItem());
+ }
+ });
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.EAST;
+ add(domainList, gbc);
+ populateDomainList();
+ setDomain((String)domainList.getSelectedItem());
+
+ JButton metadata = new JButton("Get Metadata");
+ metadata.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ try {
+ DomainMetadata dm = dom.getMetadata().getResult();
+ StringBuilder dmOutput = new StringBuilder();
+ dmOutput.append("Item Count : ");
+ dmOutput.append(dm.getItemCount());
+ dmOutput.append("\nAttr Name Count : ");
+ dmOutput.append(dm.getAttributeNameCount());
+ dmOutput.append("\nAttr Value Count : ");
+ dmOutput.append(dm.getAttributeValueCount());
+ dmOutput.append("\nItem Names Size : ");
+ dmOutput.append(dm.getItemNamesSizeBytes());
+ dmOutput.append("\nAttribute Names Size : ");
+ dmOutput.append(dm.getAttributeNamesSizeBytes());
+ dmOutput.append("\nAttribute Value Size : ");
+ dmOutput.append(dm.getAttributeValuesSizeBytes());
+ int result = JOptionPane.showInternalOptionDialog(results,
dmOutput.toString(),
+ dom.getName()+" Domain",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.PLAIN_MESSAGE,
+ null, new String [] {"Delete", "Close"}, "Close");
+ if (result == 0) {
+ result = JOptionPane.showInternalConfirmDialog(results,
+ "Do you really want to delete domain : "+dom.getName(),
+ "Delete "+dom.getName(),
+ JOptionPane.YES_NO_OPTION);
+ if (result == JOptionPane.YES_OPTION) {
+ sdb.deleteDomain(dom);
+ dom = null;
+ populateDomainList();
+ setDomain((String)domainList.getSelectedItem());
+ }
+ }
+ } catch (SDBException ex) {
+ System.err.println("Problem fetching metadata or deleting
domain: "+ex.getMessage());
+ }
+ }
+ });
+ gbc = new GridBagConstraints();
+ add(metadata, gbc);
+
+ JButton newDom = new JButton("New Domain");
+ newDom.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ try {
+ String result = JOptionPane.showInternalInputDialog(results,
+ "Enter the name of the domain you'd like to create.");
+ if (result != null) {
+ dom = sdb.createDomain(result).getResult();
+ populateDomainList();
+ }
+ } catch (SDBException ex) {
+ System.err.println("Problem creating domain : "+ex.getMessage());
+ }
+ }
+ });
+ gbc = new GridBagConstraints();
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ add(newDom, gbc);
+
+ querySpace = new JTextArea();
+ querySpace.setFont(new Font("monospaced", Font.PLAIN, 14));
+ querySpace.addKeyListener(new KeyAdapter() {
+ public void keyTyped(KeyEvent evt) {
+ if (evt.isControlDown() && evt.getKeyChar() == '\r') {
+ executeQuery();
+ }
+ }
+ });
+
+ results = new JDesktopPane();
+
+ JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new
JScrollPane(querySpace), results);
+ querySpace.setMinimumSize(new Dimension(100, 100));
+
+ gbc = new GridBagConstraints();
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.weightx = 1.0;
+ gbc.weighty = 1.0;
+ gbc.fill = GridBagConstraints.BOTH;
+ add(split, gbc);
+ }
+
+ public void populateDomainList() {
+ String [] domainNames = new String [] {"no domains found"};
+ try {
+ List<Domain> list = sdb.listDomains().getItems();
+ ArrayList<String> tmp = new ArrayList<String>();
+ for (Domain d : list) {
+ tmp.add(d.getName());
+ }
+ domainNames = tmp.toArray(domainNames);
+ } catch (SDBException ex) {
+ System.err.println("problem communicating with
SimpleDB: "+ex.getMessage());
+ System.err.println(ex.getCause().getMessage());
+ }
+ domainList.setModel(new DefaultComboBoxModel(domainNames));
+ }
+
+ public void actionPerformed(ActionEvent evt) {
+ executeQuery();
+ }
+
+ private void executeQuery() {
+ try {
+ int lineNum =
querySpace.getLineOfOffset(querySpace.getCaretPosition())+1;
+ StringTokenizer st = new StringTokenizer(querySpace.getText(), "\n",
true);
+ int lineCount = 0;
+ String val = "";
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ if (tok.equals("\n")) {
+ lineCount++;
+ }
+ else {
+ val = tok;
+ }
+ if (lineCount == lineNum) break;
+ }
+ final String query = val;
+ final ResultsFrame resultFrame = new ResultsFrame(query, dom);
+
+ results.add(resultFrame, 1);
+ try {
+ resultFrame.setSelected(true);
+ } catch (java.beans.PropertyVetoException ex) { }
+ resultFrame.show();
+
+ // start fetching the data
+ resultFrame.reload();
+ } catch (BadLocationException ex) {
+ }
+ }
+
+ private void loadPrefs() {
+ Properties props = new Properties();
+ try {
+ props.load(new
FileInputStream(System.getProperty("user.home", ".")+"/.typica.query.prefs"));
+ querySpace.setText(props.getProperty("query.history"));
+ domainList.setSelectedItem(props.getProperty("query.domain"));
+ } catch (FileNotFoundException ioex) {
+ // ignore.... might not be a file yet
+ } catch (IOException ioex) {
+ System.err.println("Error loading user preferences");
+ }
+ }
+
+ private void savePrefs() {
+ Properties props = new Properties();
+ props.setProperty("query.history", querySpace.getText());
+ props.setProperty("query.domain", (String)domainList.getSelectedItem());
+ try {
+ props.store(new
FileOutputStream(System.getProperty("user.home", ".")+"/.typica.query.prefs"),
+ "http://code.google.com/p/typica");
+ } catch (IOException ioex) {
+ System.err.println("Error saving user preferences");
+ }
+ }
+
+ public static void main(String [] args) {
+ final JFrame frame = new JFrame("SimpleDB Query Tool");
+ if (args.length != 2 && args.length != 4) {
+ System.err.println("You must supply your access id and secret key on
the command line");
+ System.err.println("Usage: QueryTool <AccessId> <SecretKey> [ProxyHost]
[ProxyPort]");
+ System.exit(-1);
+ }
+ String host = null;
+ int port = 1080;
+ try {
+ if (args.length == 4) {
+ host = args[2];
+ port = Integer.parseInt(args[3]);
+ }
+ } catch (NumberFormatException ex) {
+ System.err.println("proxy port not a valid integer, defaulting
to "+port);
+ }
+ final QueryTool controls = new QueryTool(frame, args[0], args[1], host,
port);
+ Dimension size = controls.getPreferredSize();
+ frame.setSize(800, 600);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent event) {
+ controls.shutdown();
+ System.exit(0);
+ }
+ });
+ frame.setContentPane(controls);
+ frame.setVisible(true);
+ }
+
+ // a table model that shows items from SimpleDB
+ public class ItemTableModel extends AbstractTableModel {
+ private ArrayList<String> columns;
+ private ArrayList<Item> items;
+
+ public ItemTableModel() {
+ clearData();
+ }
+
+ public void addItems(List<Item> newItems) {
+ // ensure all attrs have a column
+ int firstRow = items.size();
+ boolean colsChanged = false;
+ for (Item i : newItems) {
+ for (String name : i.getAttributes().keySet()) {
+ if (!columns.contains(name)) {
+ columns.add(name);
+ colsChanged = true;
+ }
+ }
+ }
+ items.addAll(newItems);
+ if (colsChanged) {
+ fireTableStructureChanged();
+ if (firstRow > 0) {
+ fireTableRowsInserted(firstRow, items.size());
+ }
+ }
+ }
+
+ public void clearData() {
+ columns = new ArrayList<String>();
+ columns.add("itemName()");
+ items = new ArrayList<Item>();
+ }
+
+ public int getRowCount() {
+ return items.size();
+ }
+
+ public int getColumnCount() {
+ return columns.size();
+ }
+
+ public Class getColumnClass(int column) {
+ return String.class;
+ }
+
+ public String getColumnName(int column) {
+ return columns.get(column);
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+
+ public Object getValueAt(int row, int column) {
+ if (column == 0) {
+ return items.get(row).getIdentifier();
+ }
+ else {
+ String colName = columns.get(column);
+ Set<String> vals = items.get(row).getAttributes().get(colName);
+ if (vals == null) {
+ return " -- ";
+ }
+ if (vals.size() == 1) {
+ return vals.iterator().next();
+ }
+ else {
+ StringBuilder ret = new StringBuilder();
+ for (String val : vals) {
+ ret.append(val);
+ ret.append(",");
+ }
+ return ret.toString();
+ }
+ }
+
+ }
+ }
+
+ public class ResultsFrame extends JInternalFrame implements Runnable {
+ private ItemTableModel tm;
+ private double boxUsage = 0.0;
+ private String time = "0";
+ private JLabel stats;
+ private JButton reload;
+ private JButton export;
+ private boolean countMode;
+ public JPopupMenu menu;
+ public int row;
+ public int col;
+ public Domain domain;
+
+ public ResultsFrame(String title, Domain domain) {
+ super(title);
+ this.domain = domain;
+ setClosable(true);
+ setIconifiable(true);
+ setMaximizable(true);
+ setResizable(true);
+ setBounds(0, 0, 550, 300);
+ setLayout(new BorderLayout());
+
+ JPanel topPan = new JPanel(new GridBagLayout());
+
+ reload = new JButton("reload") {
+ public int getWidth() { return getPreferredSize().width; }
+ public int getHeight() { return getPreferredSize().height; }
+ };
+ reload.setEnabled(false);
+ reload.setMargin(new Insets(0, 0, 0, 0));
+ reload.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ reload();
+ }
+ });
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.weightx = 0.5;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ topPan.add(reload, gbc);
+
+ export = new JButton("export") {
+ public int getWidth() { return getPreferredSize().width; }
+ public int getHeight() { return getPreferredSize().height; }
+ };
+ export.setEnabled(false);
+ export.setMargin(new Insets(0, 0, 0, 0));
+ export.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ export();
+ }
+ });
+ topPan.add(export, gbc);
+
+ stats = new JLabel("-");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.weightx = 1.0;
+ gbc.insets = new Insets(0, 0, 0, 5);
+ topPan.add(stats, gbc);
+
+ add(topPan, BorderLayout.NORTH);
+ tm = new ItemTableModel();
+ final JTable resultSpace = new JTable(tm);
+ try {
+ Class [] params = new Class[] { Boolean.TYPE };
+ JTable.class.getMethod("setAutoCreateRowSorter", params);
+ resultSpace.setAutoCreateRowSorter(true);
+ } catch (NoSuchMethodException ex) { } // ok, we simply won't sort
+ resultSpace.addMouseListener(new MouseAdapter() {
+ public void mousePressed(MouseEvent evt) {
+ row = resultSpace.rowAtPoint(evt.getPoint());
+ col = resultSpace.columnAtPoint(evt.getPoint());
+ menu.show(resultSpace, evt.getX(), evt.getY());
+ }
+ });
+ JScrollPane sp = new JScrollPane(resultSpace);
+ add(sp, BorderLayout.CENTER);
+
+ menu = new JPopupMenu();
+ JMenuItem copy1 = new JMenuItem("copy cell");
+ copy1.addActionListener(new ActionListener () {
+ public void actionPerformed(ActionEvent evt) {
+ copyValue((String)resultSpace.getModel().getValueAt(row, col));
+ }
+ });
+ JMenuItem copy2 = new JMenuItem("copy row");
+ copy2.addActionListener(new ActionListener () {
+ public void actionPerformed(ActionEvent evt) {
+ StringBuilder sb = new StringBuilder();
+ AbstractTableModel tm = (AbstractTableModel)resultSpace.getModel();
+ for (int i=0; i<tm.getColumnCount(); i++) {
+ if (i > 0) sb.append("\t");
+ sb.append(tm.getValueAt(row, i));
+ }
+ copyValue(sb.toString());
+ }
+ });
+ menu.add(copy1);
+ menu.add(copy2);
+ }
+
+ public void copyValue(String value) {
+ Clipboard clip = getToolkit().getSystemClipboard();
+ StringSelection newData = new StringSelection(value);
+ clip.setContents(newData, newData);
+ }
+
+ public void reload() {
+ reload.setEnabled(false);
+ export.setEnabled(false);
+ boxUsage = 0.0;
+ time = "0";
+ updateStats();
+ tm.clearData();
+ new Thread(this).start();
+ }
+
+ public void export() {
+ // file picker
+ JFileChooser chooser = new JFileChooser(".");
+ chooser.setMultiSelectionEnabled(false);
+ int result = chooser.showSaveDialog(parent);
+ if (result == JFileChooser.APPROVE_OPTION) {
+ // scan table model and emit file
+ File exportFile = chooser.getSelectedFile();
+ if (exportFile.exists()) {
+ // ask about overwrite
+ }
+ try {
+ PrintWriter pw = new PrintWriter(exportFile);
+ int numColumns = tm.getColumnCount();
+ int numRows = tm.getRowCount();
+ for (int j=0; j<numColumns; j++) {
+ pw.print(tm.getColumnName(j));
+ if (j < (numColumns-1)) {
+ pw.print(",");
+ }
+ }
+ pw.println("");
+ for (int i=0; i<numRows; i++) {
+ for (int j=0; j<numColumns; j++) {
+ pw.print(tm.getValueAt(i, j).toString());
+ if (j < (numColumns-1)) {
+ pw.print(",");
+ }
+ }
+ pw.println("");
+ }
+ pw.close();
+ } catch (IOException ex) {
+ System.err.println("Error exporting data: "+ex.getMessage());
+ }
+ }
+ }
+
+ public void run() {
+ StringBuilder resText = new StringBuilder();
+ try {
+ int itemCount = 0;
+ long start = System.currentTimeMillis();
+ String nextToken = null;
+ countMode = (getTitle().indexOf("count(*)") > -1);
+ long total = 0;
+ do {
+ SDBListResult<Item> sr = domain.selectItems(getTitle(), nextToken,
false);
+ List<Item> items = sr.getItems();
+ nextToken = sr.getNextToken();
+ itemCount += items.size();
+ if (countMode) {
+ total += Long.parseLong(items.get(0).getAttribute("Count"));
+ }
+ updateResults(items);
+ updateBoxUsage(sr.getBoxUsage());
+ if (itemCount > 1000) {
+ nextToken = null;
+ ArrayList<Item> trunc = new ArrayList<Item>();
+ trunc.add(domain.getItem("- truncated -").getResult());
+ updateResults(trunc);
+ }
+ } while (nextToken != null && !nextToken.trim().equals(""));
+ long end = System.currentTimeMillis();
+ updateTime((end-start)/1000.0);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ reload.setEnabled(true);
+ export.setEnabled(true);
+ }
+ });
+ if (countMode) {
+ List<Item> tmp = new ArrayList<Item>();
+ Item i = new ItemVO("Total");
+ HashSet<String> vals = new HashSet<String>();
+ vals.add(""+total);
+ i.getAttributes().put("Count", vals);
+ tmp.add(i);
+ updateResults(tmp);
+ }
+ } catch (SDBException ex) {
+ resText.append(ex.getMessage());
+ }
+ }
+
+ void updateResults(final List<Item> data) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ tm.addItems(data);
+ updateStats();
+ }
+ });
+ }
+
+ void updateBoxUsage(final String usage) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ double newUsage = Double.parseDouble(usage);
+ boxUsage += newUsage;
+ updateStats();
+ } catch (NumberFormatException ex) {
+ System.err.println("error parsing box usage : "+ex.getMessage());
+ }
+ }
+ });
+ }
+
+ void updateTime(final double timeVal) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ time = ""+timeVal;
+ updateStats();
+ }
+ });
+ }
+
+ private void updateStats() {
+ String usage = ""+boxUsage;
+ if (usage.length() > 10) usage = usage.substring(0, 10);
+ stats.setText("Box Usage:"+usage+" Items:"+tm.getRowCount()+" Time To
Run:"+time+" secs");
+ }
+ }
+}
=======================================
--- /trunk/build.xml Sat Apr 10 06:39:58 2010
+++ /trunk/build.xml Tue Apr 13 07:39:30 2010
@@ -9,6 +9,7 @@
<property name="typica.jar" value="${typica.build}/jar" />
<property name="typica.jar.file" value="${typica.jar}/typica.jar" />
<property name="typica.sdbshell.jar.file" value="${typica.jar}/sdbShell"
/>
+ <property name="typica.querytool.jar.file"
value="${typica.jar}/QueryTool" />
<property name="typica.classes" value="${typica.build}/classes" />
<property name="typica.jar.classes" value="${typica.jar}/classes" />
<property name="typica.generated" value="${typica.build}/generated" />
@@ -188,7 +189,7 @@
<include name="jaxb-api.jar"/>
<include name="jaxb-impl.jar"/>
<include name="jsr173_1.0_api.jar"/>
- <include name="commons-logging-1.4.jar"/>
+ <include name="commons-logging-1.1.1.jar"/>
<include name="commons-codec-1.4.jar"/>
<include name="httpclient-4.0.jar"/>
<include name="httpcore-4.0.1.jar"/>
@@ -206,6 +207,33 @@
</jar>
</target>
+ <target name="querytool" depends="compile, test.compile">
+ <input message="Enter the version # : " addproperty="rel.version"/>
+ <antcall target="test.compile">
+ <param name="test.src.path" value="test/java/sdbShell.java"/>
+ </antcall>
+ <mkdir dir="${typica.jar.classes}"/>
+ <unzip dest="${typica.jar.classes}">
+ <fileset dir="${typica.lib}">
+ <include name="activation.jar"/>
+ <include name="jaxb-api.jar"/>
+ <include name="jaxb-impl.jar"/>
+ <include name="jsr173_1.0_api.jar"/>
+ <include name="commons-logging-1.1.1.jar"/>
+ <include name="commons-codec-1.4.jar"/>
+ <include name="httpclient-4.0.1.jar"/>
+ <include name="httpcore-4.0.1.jar"/>
+ </fileset>
+ </unzip>
+ <jar destfile="${typica.querytool.jar.file}${rel.version}.jar">
+ <fileset dir="${typica.classes}" includes="**/*" excludes="**/ec2/*,
**/sqs/*" />
+ <fileset dir="${typica.jar.classes}" includes="**/*" />
+ <manifest>
+ <attribute name="Main-Class" value="QueryTool"/>
+ </manifest>
+ </jar>
+ </target>
+
<target name="test.main" depends="test.compile">
<java fork="true" classname="${class}">
<classpath>
=======================================
--- /trunk/java/com/xerox/amazonws/common/AWSConnection.java Sat Apr 10
06:39:58 2010
+++ /trunk/java/com/xerox/amazonws/common/AWSConnection.java Tue Apr 13
07:39:30 2010
@@ -87,27 +87,27 @@
}
}
- protected String getAwsAccessKeyId() {
+ public String getAwsAccessKeyId() {
return this.awsAccessId;
}
- protected String getSecretAccessKey() {
+ public String getSecretAccessKey() {
return this.awsSecretKey;
}
- protected boolean isSecure() {
+ public boolean isSecure() {
return this.isSecure;
}
- protected String getServer() {
+ public String getServer() {
return this.server;
}
- protected void setServer(String server) {
+ public void setServer(String server) {
this.server = server;
}
- protected int getPort() {
+ public int getPort() {
return this.port;
}
=======================================
--- /trunk/java/com/xerox/amazonws/common/TypicaHttpClient.java Sat Apr 10
06:39:58 2010
+++ /trunk/java/com/xerox/amazonws/common/TypicaHttpClient.java Tue Apr 13
07:39:30 2010
@@ -84,7 +84,6 @@
}
}
}
- System.err.println("target host = "+targethost.getHostName());
request.addHeader(HTTP.TARGET_HOST, targethost.getHostName());
}
}
=======================================
--- /trunk/test/java/ShowQueueLoadHistory.java Mon Mar 24 17:39:28 2008
+++ /trunk/test/java/ShowQueueLoadHistory.java Tue Apr 13 07:39:30 2010
@@ -2,9 +2,9 @@
import java.util.ArrayList;
import java.util.Properties;
-import com.xerox.amazonws.sqs.MessageQueue;
-import com.xerox.amazonws.sqs.QueueService;
-import com.xerox.amazonws.sqs.SQSException;
+import com.xerox.amazonws.sqs2.MessageQueue;
+import com.xerox.amazonws.sqs2.QueueService;
+import com.xerox.amazonws.sqs2.SQSException;
// measures load average on queues at 1, 5 and 15 minutes
public class ShowQueueLoadHistory {