Sunday, September 06, 2009

Amazon Product Advertising API

Product Advertising API Signed Requests - Java SOAP using Spring Web Services

Product Advertising API - https://affiliate-program.amazon.com/gp/advertising/api/detail/main.html/178-0006257-2456255

We are using Amazon Web Services to advertise products on one of my clients websites. We used Springframework as the dependency container and Spring Web Services as web service platform as we Spring-WS favors contract-first web services. With the recent changes in the API all the requests must be signed. And the sample code (http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2479&categoryID=14) helped me as a reference to migrate our code base to support the signed request requirement.

In the below sections I will walk through the steps to make signed requests using Spring Web Services.

Project Setup:

We used maven2 as our build & reporting tool we followed the maven project directory structure and is as below:

awsprocess
└───src
    ├───main
    │   ├───java
    │   │   └───com
    │   │       └───ascendant76
    │   │           └───awsdemo
    │   │               ├───client
    │   │               └───service
    │   ├───resources
    │   └───webapp
    └───test
        ├───java
        └───resources

The project object model uses few plug-ins:

maven-compiler-plugin: To compile the code that is compatible with J2SE 5.0.

maven-surefire-plugin: To execute the unit test cases. I used TestNG(http://testng.org/doc/index.html) for the test cases and their by the testng dependency is imported.

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <classifier>jdk15</classifier>
            <version>5.9</version>
        </dependency>

maven-jaxb2-plugin: Creates JAXB2 binding from XSD. The plug-in scans the xjb files and generates the bindings in our case it AWSECommerceService.xjb in the resources folder.

AWSECommerceService.xjb:

<?xml version='1.0' encoding='utf-8' ?>
<jxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <jxb:bindings node="/xs:schema"
        schemaLocation="AWSECommerceService.xsd">
        <jxb:globalBindings fixedAttributeAsConstantProperty="false"
            collectionType="java.util.ArrayList" typesafeEnumBase="xs:NCName"
            choiceContentProperty="false" typesafeEnumMemberName="generateError"
            enableFailFastCheck="true" generateIsSetMethod="true"
            underscoreBinding="asCharInWord"
            enableJavaNamingConventions="true">
        </jxb:globalBindings>
        <jxb:schemaBindings>
            <jxb:package name="com.ascendant76.awsdemo.model" />
        </jxb:schemaBindings>
    </jxb:bindings>
</jxb:bindings>

The AWSECommerceService.xsd is extracted manually from the web service contract schema from the WSDL located at http://ecs.amazonaws.com/AWSECommerceService/2009-07-01/AWSECommerceService.wsdl

Account Setup:

Login to amazon at http://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=activity-summary.  Goto Access Credentials section by following this URL https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=access-key. This page allows you to create new X.509 certificates. Select the Create option to create your certificates and download them. You will have two files pk-XXXX.pem and cert-XXXX.pem
 
Note: You can download the private key file (pk-XXXX.pem) at the time of certificate creation only and will not be available later. If you lose it, you have to create a new certificate. So keep it safe.

Now we need to convert the certificate into the PCKS12 format. We need openssl (http://www.openssl.org) to create the certificate in pcks12 format.

C:\openssl pkcs12 -export -in cert-XXXX.pem -inkey pk-XXXX.pem -name ramesh_cert -out ramesh_cert.p12

The pkcs12 file will be generated. Execute the following command to verify the generated certificate using the user name.
 
C:\jdk1.6.0_13\bin\keytool -v -list -storetype pkcs12 -keystore ramesh_cert.p12

Enter keystore password:  ramesh

The output appears something like below:

Keystore type: pkcs12
Keystore provider: SunJSSE

Your keystore contains 1 entry

Alias name: ramesh_cert
Creation date: Aug 28, 2009
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=zzp69oziqnw6, OU=AWS-Developers, O=Amazon.com, C=US
Issuer: CN=AWS Limited-Assurance CA, OU=AWS, O=Amazon.com, C=US
Serial number: d61749a157
Valid from: Fri Aug 28 16:49:58 EDT 2009 until: Sat Aug 28 16:49:58 EDT 2010
Certificate fingerprints:
         MD5:  60:BF:22:AF:51:F8:5A:72:36:8F:E8:69:1D:35:8C:C0
         SHA1: D0:D9:5A:60:38:35:98:45:C3:41:CE:1A:8F:76:DC:39:80:3A:02:1B
**************************************************************************************


Once you verify the certificate generated is okay with the given user
name, copy the certificate generated above - ramesh_cert.p12 - into the
resources directory.

Codebase:

The dependency diagram of various collaborators is given below.


The AwsGateway is the sub-class of org.springframework.ws.client.core.support.WebServiceGatewaySupport  is responsible for marshaling the java objects in to SOAP messages and sends to location mentioned in the service binding of the WSDL and in this case https://ecs.amazonaws.com/onca/soap?Service=AWSECommerceService. The WebServiceGatewaySupport delegates the  responsibility to the Marshaller to marshal java request objects to SOAP messages and UnMarshaller to marshal the SOAP response  back to Java object.


    <!-- Working with the request and response xml's using Jaxb2 -->
    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="schema" value="classpath:AWSECommerceService.xsd" />
        <property name="contextPath" value="com.ascendant76.awsdemo.model" />
    </bean>

Spring-WS supports two types of messages factories to deal with SOAP messages org.springframework.ws.soap.axiom.AxiomSoapMessageFactory and org.springframework.ws.soap.saaj.SaajSoapMessageFactory. And I choose to use SAAJ (https://saaj.dev.java.net/nonav/spec-1.3/api/).
  
    <!-- SOAP with Attachments API for Java -->
    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />

Now the web service gateway definition looks as below:

    <bean id="awsGateway" class="com.ascendant76.awsdemo.service.AwsGateway">
        <constructor-arg value="${aws.accessKey}" />
        <property name="defaultUri" value="https://ecs.amazonaws.com/onca/soap?Service=AWSECommerceService" />
        <property name="marshaller" ref="marshaller" />
        <property name="unmarshaller" ref="marshaller" />
        <property name="messageFactory" ref="messageFactory" />
        <property name="interceptors">
            <list>
                <ref bean="signatureSecurityInterceptor" />
            </list>
        </property>
    </bean>

 As security is an orthogonal concept, spring-ws handles it using interceptors. We use WSS4J for the authentication to produce signed requests. Amazon API rely on Timestamp and Signature.

    <bean id="signatureSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
        <property name="securementActions" value="Timestamp Signature" />
        <property name="timestampPrecisionInMilliseconds" value="true" />
        <property name="securementSignatureKeyIdentifier" value="DirectReference" />
        <property name="securementUsername" value="${aws.keystoreUser}" />
        <property name="securementPassword" value="${aws.keystorePassword}" />
        <property name="securementSignatureCrypto" ref="cryptoFactoryBean" />
        <property name="securementCallbackHandler" ref="passwordCallback" />
    </bean>

    <bean id="cryptoFactoryBean" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
        <property name="cryptoProvider" value="org.apache.ws.security.components.crypto.Merlin" />
        <property name="keyStoreLocation" value="classpath:${aws.keystoreUser}.p12" />
        <property name="keyStorePassword" value="${aws.keystorePassword}" />
        <property name="keyStoreType" value="pkcs12" />
        <property name="keyStoreProvider" value="SunJSSE" />
    </bean>

    <bean id="passwordCallback" class="com.ascendant76.awsdemo.service.PasswordCallback">
        <constructor-arg index="0" value="${aws.keystorePassword}" />
    </bean>

To keep all the properties in one place we can use property place holder. The keystore password is being served from properties file:

    <context:property-placeholder location="classpath:app.properties" />

Change the properties files to reflect your credentials

# Amazon Associate Access Key
aws.accessKey=<<AWS ACCESSKEY comes here>>
aws.keystoreUser=<<KEY STORE comes here>>
aws.keystorePassword=<<KEY STORE password>>

Now at this stage we are ready to invoke the web service. Execute mvn clean test to execute the test cases.

Download the code
You can download the code from here: Download the Code


References:
http://www.openssl.org/docs/apps/pkcs12.html

Tuesday, August 25, 2009

Nandan Nilekani's ideas for India's future

Interesting video by Nandan Nilekani and his ideas for India's future. The problem domain is mapped to the following categories.

* Ideas that have arrived
* Ideas in progress
* Ideas in conflict
* Ideas in anticipation

What I like in this presentation is the approach based on the above four fundamentals.

Thursday, August 06, 2009

Don't mess with Java naming convensions - Java Generics

Today I found the following program over the internet. First time when I saw it I thought it won't compile.

import java.util.ArrayList;
 
public class ListProvider {
  public static ArrayList sayHello(ArrayList ar) {
    return ar;
  }
  public static void main(String[] args) {
     System.out.println(sayHello("I am a list"));
  }
}
 
Now I rewrote the program following java naming conventions. 
 
public class ListProvider {
  public static T sayHello(T ar) {
    return ar;
  }
  public static void main(String[] args) {
     System.out.println(sayHello("I am a list"));
  }
}

is a Generic Type. It is hiding java.util.ArrayList in the first program. Second program it is bit clean using T as type.

Sunday, June 28, 2009

Interesting Quote

Re-use before Buy
Buy before build,
Build for Re-use
               
- John Zachman

Tuesday, May 19, 2009

Checking the file association in Windows from Command line

Checking the file association in Windows:

The "assoc" will let you know the file type associated with the extension
C:\>assoc .txt
.txt=txtfile

Now the "ftype" displays or modifies file types used in file extension associations
C:\>ftype txtfile
txtfile=%SystemRoot%\system32\NOTEPAD.EXE %1

Sunday, October 19, 2008

When two in Tango ...



When you look in to my eyes...
.. eye lids stop flittering
.. heart beats fast
.. minds freezes the frame of yours in my eye lids

Let us tango together..
.. the way no one did
.. the way no one will
.. let the eyes be in to my eyes

Let us not stop this...
.. as long as the clock ticks
.. as long as the earth rotates
.. as long as we can with stand standing

If you feel it is the last step we make ..
.. let our eyes lock together
.. let the warm hug seize the air separting us
.. let the God feel jealous of us

Let the tago continue ...
Let the eyes lock one another...
Let the time ends ...

Appropriate English Usage 1

When some one acknowledges me by saying "Thank you!",  my immediate reply used to be "No problem!". Today I learned that it is not traditional and sounds less polite.

Tuesday, October 14, 2008

Yahoo Query Language (YQL)

You may be interested of this Yahoo Query Language (YQL)  . This is really intereting as they are not exposing their database tables but enabling their domain model ( structured data) using SQL. Behind the scenes they can map to their tables or what ever it could be.

Yahoo! makes a lot of structured data available to developers, primarily through its web services. These services require developers to locate the right URLs and documentation to access and query them which can result in a very fragmented experience. The YQL platform provides a single endpoint service that enables developers to query, filter and combine data across Yahoo! and beyond. YQL exposes a SQL-like SELECT syntax that that is both familiar to developers and expressive enough for getting the right data. Through the SHOW and DESC commands we enable developers to discover the available data sources and structure without opening another web browser.

Wednesday, October 08, 2008

No One Needs to Know by Shania Twain

Tuesday, October 07, 2008

OMG - I Will Kill Her


Is that Agony or Jealousy?