I’m going to show you how to create a consumer for an Amazon Web Service.
First, we need a WSDL, in this example I’ll use this one: http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl
Then, we need to install Eclipse with the Turmeric Eclipse Plugin
Now the good stuff. First, start by opening Eclipse. Then, go to New -> Others -> Turmeric SOA -> New Consumer from WSDL

New consumer from wsdl
Now, in the next screen, set the WSDL and other info as the screenshot below:

New consumer from wsdl wizard
Now, press Finish and wait for the plugin to work its magic. Once the plugin finishes to create the required artifacts, your workspace should look something like this:

Turmeric Workspace
Notice the 2 projects? those are the Service (AwsdnAWSECommerceServiceV1) and Consumer (AWSECommerceServiceV1Consumer) projects.
One thing we need to change is the maven dependencies in the projects. This is due to a recently fixed bug in the soa-client code. So, we need to make suere we are using the following maven dependencies in the projects:
...
<dependency>
<groupId>org.ebayopensource.turmeric.runtime</groupId>
<artifactId>soa-client</artifactId>
<version>1.0.0-Beta-RC1</version>
</dependency>
...
and the maven-turmeric-plugin dependency:
...
<groupId>org.ebayopensource.turmeric.maven</groupId>
<artifactId>turmeric-maven-plugin</artifactId>
<version>1.0.0-Beta-RC1</version>
...
To call the Amazon Web Service in our consumer, We need to have an account in Amazon Webservices to have access to the service. For that, I simply went here http://aws.amazon.com/ and created an AWS Account. Once created, you should have an Access Key ID and a Secret Access Key for your account. Then, We need to send 3 parameters in the SOAP header of the request to the service:
- AWSAccessKeyId: Your access key
- Timestamp: the current time at the moment of making the call. It needs to be in the format:
YYYY-MM-DDThh:mm:ssZ
- Signature: This is the tricky one. This parameter is the HMAC-SHA256 signature calculated from the concatenation of the
Action and Timestamp parameters. For instance, if I want to call the ItemSearch operation in the webservice, I would have to do this: Signature = HMAC-SHA256(ItemSearch2010-10-16T00:00:00Z)
A more detailed explanation of the soap headers is here: http://docs.amazonwebservices.com/AWSECommerceService/2010-11-01/DG/index.html?AuthJavaSampleSig2.html
An example request to this service would look like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://webservices.amazon.com/AWSECommerceService/2010-12-01">
<soapenv:Header xmlns:aws="http://security.amazonaws.com/doc/2007-01-01/">
<aws:AWSAccessKeyId>your access key here</aws:AWSAccessKeyId>
<aws:Timestamp>2011-03-15T15:38:23Z</aws:Timestamp>
<aws:Signature>some generated signature</aws:Signature>
</soapenv:Header>
<soapenv:Body>
<ns:ItemSearch>
<ns:Request>
<ns:SearchIndex>Books</ns:SearchIndex>
<ns:Keywords>Harry%20Potter</ns:Keywords>
</ns:Request>
</ns:ItemSearch>
</soapenv:Body>
</soapenv:Envelope>
Ok, now, the consumer code. Inside the consumer project, create a simple java class called AWSConsumer. Then, make it extend this class: SharedAwsdnAWSECommerceServiceV1Consumer. This class is one of the elements the Turmeric Plugin created for us in the service project. If you take a look at the service project, it should look like this:
public class AWSConsumer extends SharedAwsdnAWSECommerceServiceV1Consumer{
public AWSConsumer(String clientName) throws ServiceException {
super(clientName);
}
public static void main(String[] args) throws ServiceException{
//nothing yet
}
}

Service Project layout
Now, to call the service, we need to create the Signature parameter and placing it in the soap header. For this, we need to create a helper class. I created this helper class based on this example: http://docs.amazonwebservices.com/AWSECommerceService/2010-11-01/DG/index.html?TheStructureofaSOAPRequest.html
This is the code for the helper class:
package com.amazon.aws.util;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class SignedRequestHelper {
private static final String UTF8_CHARSET = "UTF-8";
private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
private static final String REQUEST_URI = "/onca/xml";
private static final String REQUEST_METHOD = "GET";
private String endpoint = "ecs.amazonaws.com"; // must be lowercase
private String awsAccessKeyId = "Your access key here";
private String awsSecretKey = "Your secret key here";
private SecretKeySpec secretKeySpec = null;
private Mac mac = null;
private String currentTimestamp = null;
private String operation = null;
public SignedRequestHelper(String operationToSign)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException {
this.operation = operationToSign;
byte[] secretyKeyBytes = awsSecretKey.getBytes(UTF8_CHARSET);
secretKeySpec = new SecretKeySpec(secretyKeyBytes,
HMAC_SHA256_ALGORITHM);
mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(secretKeySpec);
}
private String hmac(String stringToSign) {
String signature = null;
byte[] data;
byte[] rawHmac;
try {
data = stringToSign.getBytes(UTF8_CHARSET);
rawHmac = mac.doFinal(data);
Base64 encoder = new Base64();
signature = new String(encoder.encode(rawHmac));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(UTF8_CHARSET + " is unsupported!", e);
}
return signature;
}
public String getTimestamp() {
if (currentTimestamp == null) {
Calendar cal = Calendar.getInstance();
DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
currentTimestamp = dfm.format(cal.getTime());
}
return currentTimestamp;
}
public String getSignature() {
return this.hmac(this.operation + this.getTimestamp());
}
public static void main(String[] args) throws InvalidKeyException,
UnsupportedEncodingException, NoSuchAlgorithmException {
SignedRequestHelper helper = new SignedRequestHelper("ItemSearch");
SortedMap<String, String> sortedParamMap = new TreeMap<String, String>();
sortedParamMap.put("Action", "ItemSearch");
String timestamp = helper.getTimestamp();
System.out.println("timestamp = " + timestamp);
sortedParamMap.put("TimeStamp", timestamp);
System.out.println(helper.hmac("ItemSearch" + timestamp));
}
}
Now, let’s add the headers in the consumer code and do the call in the consumer code:
package com.amazon.aws.consumer;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.runtime.binding.objectnode.impl.JavaObjectNodeImpl;
import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException;
import com.amazon.aws.util.SignedRequestHelper;
import com.amazon.webservices.awsecommerceservice._2010_12_01.Item;
import com.amazon.webservices.awsecommerceservice._2010_12_01.ItemSearch;
import com.amazon.webservices.awsecommerceservice._2010_12_01.ItemSearchRequest;
import com.amazon.webservices.awsecommerceservice._2010_12_01.ItemSearchResponse;
import com.amazon.webservices.awsecommerceservice._2010_12_01.Items;
import com.amazon.webservices.awsecommerceservice._2010_12_01.awsecommerceservice.gen.SharedAwsdnAWSECommerceServiceV1Consumer;
public class AWSConsumer extends SharedAwsdnAWSECommerceServiceV1Consumer{
public AWSConsumer(String clientName) throws ServiceException {
super(clientName);
}
public static void main(String[] args) throws ServiceException, InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException{
AWSConsumer consumer = new AWSConsumer("AWSECommerceServiceV1Consumer");
ItemSearch itemSearch = new ItemSearch();
ItemSearchRequest itemSearchRequest = new ItemSearchRequest();
itemSearchRequest.setSearchIndex("Books");
itemSearchRequest.setKeywords("Harry Potter");
itemSearch.getRequest().add(itemSearchRequest);
SignedRequestHelper helper = new SignedRequestHelper("ItemSearch");
String signature = helper.getSignature();
System.out.println("signature="+signature);
System.out.println("timestamp="+helper.getTimestamp());
JavaObjectNodeImpl soapHeaderSignatureParam = new JavaObjectNodeImpl(new QName("http://security.amazonaws.com/doc/2007-01-01/", "Signature"), signature);
consumer.getService().getRequestContext().addMessageHeader(soapHeaderSignatureParam);
JavaObjectNodeImpl soapHeaderAWSAccessKeyIdParam = new JavaObjectNodeImpl(new QName("http://security.amazonaws.com/doc/2007-01-01/","AWSAccessKeyId"),"AKIAI3VIWRGZUL2VCDOA");
consumer.getService().getRequestContext().addMessageHeader(soapHeaderAWSAccessKeyIdParam);
JavaObjectNodeImpl soapHeaderTimestampParam = new JavaObjectNodeImpl(new QName("http://security.amazonaws.com/doc/2007-01-01/", "Timestamp"),helper.getTimestamp());
consumer.getService().getRequestContext().addMessageHeader(soapHeaderTimestampParam);
consumer.getService().getInvokerOptions().setMessageProtocolName("SOAP11");
consumer.getService().getRequestContext().setTransportHeader("SOAPAction", "http://soap.amazon.com/ItemSearch");
ItemSearchResponse response = consumer.itemSearch(itemSearch);
for (Items item : response.getItems()) {
for (Item books : item.getItem()) {
System.out.println("book title = "+books.getItemAttributes().getTitle());
}
}
}
}
One more thing. The platform uses a file called ClientConfig.xml. This file is used in the consumer code to configure things like protocol to use, message protocols and the url for the service. This file is found here: /AWSECommerceServiceV1Consumer/meta-src/META-INF/soa/client/config/AWSECommerceServiceV1Consumer/production/AwsdnAWSECommerceServiceV1/ClientConfig.xml
To make the call to the Amazon Web Service, you need to configure your ClientConfig.xml file like this:
<?xml version="1.0" encoding="UTF-8"?>
<client-config-list xmlns="http://www.ebayopensource.org/turmeric/common/config">
<client-config>
<service-interface-class-name>com.amazon.webservices.awsecommerceservice._2010_12_01.awsecommerceservice.AwsdnAWSECommerceServiceV1</service-interface-class-name>
<service-location>https://ecs.amazonaws.com/onca/soap?Service=AWSECommerceService</service-location>
<client-instance-config>
<invocation-options>
<preferred-transport name="HTTP11" />
<request-data-binding>XML</request-data-binding>
<response-data-binding>XML</response-data-binding>
<consumer-id>MyConsumerID</consumer-id>
<message-protocol>SOAP11</message-protocol>
</invocation-options>
<protocol-processor version="1.1" name="SOAP11">
<indicator>
<transport-header name="X-EBAY-SOA-MESSAGE-PROTOCOL">SOAP11</transport-header>
</indicator>
<class-name>org.ebayopensource.turmeric.runtime.sif.impl.protocolprocessor.soap.ClientSOAPProtocolProcessor</class-name>
</protocol-processor>
</client-instance-config>
</client-config>
</client-config-list>
Now, It’s just a matter of building the projects. For this I use: Run As -> Maven Install option inside Eclipse. Then, just run the consumer with the Run As -> Java program. The output should be like this:
signature=jt4Sdl4ur4EMyyUtdLhla2/4htDNGuJCuI8BBETvkho=
timestamp=2011-03-19T15:37:53Z
Found config file at jar:file:/home/manuelchinea/.m2/repository/com/ebay/kernel/uKernelCore/1.73/uKernelCore-1.73.jar!/config/ukernelcore/logging.propertiesMar 19, 2011 11:07:53 AM org.ebayopensource.turmeric.runtime.common.impl.utils.ParseUtils getFileStream
WARNING: Unable to find resource: META-INF/soa/common/config/GlobalRegistry.xml
Found config file at jar:file:/home/manuelchinea/.m2/repository/com/ebay/kernel/uKernelCore/1.73/uKernelCore-1.73.jar!/config/ukernelcore/ConfigCommandListener.properties
Found config file at jar:file:/home/manuelchinea/.m2/repository/com/ebay/kernel/uKernelCore/1.73/uKernelCore-1.73.jar!/config/ukernelcore/ConfigCommandListener.properties
Found config file at jar:file:/home/manuelchinea/.m2/repository/com/ebay/kernel/uKernelCore/1.73/uKernelCore-1.73.jar!/config/ukernelcore/ConfigCommandListener.properties
Found config file at jar:file:/home/manuelchinea/.m2/repository/com/ebay/kernel/uKernelCore/1.73/uKernelCore-1.73.jar!/config/ukernelcore/ConfigCommandListener.properties
book title = Harry Potter: A Pop-Up Book: Based on the Film Phenomenon
book title = Harry Potter Paperback Box Set (Books 1-7)
book title = Harry Potter and the Sorcerer's Stone: 10th Anniversary Edition (Harry Potter)
book title = Harry Potter and the Deathly Hallows (Book 7)
book title = Harry Potter and the Half-Blood Prince (Book 6)
book title = Harry Potter And The Order Of The Phoenix
book title = Harry Potter and the Prisoner of Azkaban (Book 3)
book title = Harry Potter and the Goblet of Fire (Book 4)
book title = Harry Potter and the Chamber of Secrets (Book 2)
book title = The Unofficial Harry Potter Cookbook: From Cauldron Cakes to Knickerbocker Glory--More Than 150 Magical Recipes for Muggles and Wizards