Friday, November 15, 2013

Google App Engine Maven setup


Maven Setup for Google App Engine

Playing with Google App Engine to setup simple Spring application .It's  simple to setup simple Servlet application through Eclipse plugin but I don't want to maintain dependency in eclipse , got to have Maven . It's little tricky to setup , so here is the info . 

  1. Download AppEngine ( Not just the Eclipse plugin for gae )  
  2. Setup Maven master password and google password as encrypted value 
  3. pom.xml

Password Setup

Setup the master password and the google password .Details for setting up password explained here 
Your settings-security.xml and settings.xml has to be updated . Provided mine below for reference
settings-security.xml

<settingsSecurity>
 <master>encryptedvaluehere</master>
</settingsSecurity>

Settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <localRepository/>
  <interactiveMode/>
  <usePluginRegistry/>
  <offline/>
  <pluginGroups/>
  <servers>
      <server>
            <id>appengine.google.com</id>
            <username>emailhere</username>
            <password>encryptedvaluehere</password>
        </server>
  </servers>
  <mirrors/>
  <proxies/>
  <profiles/>
  <activeProfiles/>
</settings>


Directory

My case the source directory is src , not src/main and all the other required files are in src main as shown below



pom.xml

Specify the Google App Engine home in gae.home tag.
Enter the group and artifact id
mvn gae:deploy will deploy the application to the app engine
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<version>1.0</version>


<groupId>entergroupid</groupId>
<artifactId>enterartifactid</artifactId>

<pluginRepositories>
<pluginRepository>
<id>maven-gae-plugin-repo</id>
<name>Maven Google App Engine Repository</name>
<url>http://maven-gae-plugin.googlecode.com/svn/repository/</url>
</pluginRepository>
<pluginRepository>
<id>google-staging</id>
<name>Google Staging</name>
<url>https://oss.sonatype.org/content/repositories/comgoogleappengine-1004/</url>
</pluginRepository>
</pluginRepositories>

<repositories>
<repository>
<id>google-maven-repo</id>
<name>Maven Google App Engine Repository</name>
<url>http://maven-gae-plugin.googlecode.com/svn/repository/</url>
</repository>
<repository>
<id>codehaus</id>
<name>codehaus</name>
<url>http://repository.codehaus.org/org/codehaus/groovy/</url>
</repository>
<repository>
<id>repository.springsource.release</id>
<name>SpringSource GA Repository</name>
<url>http://repo.springsource.org/release</url>
</repository>
</repositories>


<properties>
<appengine.app.version>1</appengine.app.version>
<appengine.target.version>1.8.6</appengine.target.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-framework.version>3.1.2.RELEASE</spring-framework.version>
<spring-integration.version>2.1.3.RELEASE</spring-integration.version>
<spring-ws.version>2.1.0.RELEASE</spring-ws.version><!--2.1.0.RELEASE-->
<spring-security-version>3.1.2.RELEASE</spring-security-version>
<enunciate.version>1.27</enunciate.version>
<gae.version>1.8.6</gae.version>
<gae.port>9090</gae.port>
<net.kindleit.version>0.9.6-SNAPSHOT</net.kindleit.version>
<gae.home>/Library/appengine/appengine-java-sdk-${gae.version}</gae.home>
<webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>

</properties>

<dependencies>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.target.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<dependency>
<groupId>org.codehaus.enunciate</groupId>
<artifactId>enunciate-rt</artifactId>
<version>${enunciate.version}</version>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src/resources</directory>
</resource>
</resources>
<outputDirectory>target/${project.artifactId}-${project.version}/WEB-INF/classes</outputDirectory>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webResources>
<resource>
<directory>src/resources</directory>
</resource>
</webResources>
</configuration>
</plugin>

<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>${gae.version}</version>
<configuration>
<fullScanSeconds>1</fullScanSeconds>
<server>preview.appengine.google.com</server>
</configuration>
</plugin>



<plugin>
<groupId>net.kindleit</groupId>
<artifactId>maven-gae-plugin</artifactId>
<version>0.9.2</version>
<configuration>
<unpackVersion>${gae.version}</unpackVersion>
<serverId>appengine.google.com</serverId>
</configuration>
<dependencies>
<dependency>
<groupId>net.kindleit</groupId>
<artifactId>gae-runtime</artifactId>
<version>${gae.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.2.2</version>
<configuration>
<goals>gae:deploy</goals>
</configuration>
</plugin>


</plugins>
</build>



</project>

Tuesday, November 12, 2013

Spring Integration - Calling SOAP Service using Gateway , Channel and Chain


WebService using Spring Integration

Article to show WebService call ( SOAP ) using  Spring Integration . It uses  Gateway ,  Channel and Router

What you will need 
1. Schema or WSDL to generate the Marshaller and UnMarshaller Objects . 
2. Java Interface to Call WebService
3. Bean configuration defining  gateway , channel , router etc
4. Java Transformer 

Generation of Marshaller and UnMarshaller

Marshaller and a UnMarshaller are basically Java representation of the Request and Response  , these can be generated by xjc utility on schema (or) wsimport on WSDL. If you don't have the schema , it can be generated by the xml using many tools like http://www.freeformatter.com/  Here is example of generating them from schema  using ant ( uses xjc )
 
  <?xml version = "1.0" encoding = "UTF-8"?>

<project name = "mywsproject" 
        default = "generate">
 <property file = "build.properties"/>
 
  <fileset id = "tools" 
           dir = "tools">
    <include name = "**/*.jar"/>
  </fileset> 
 
 <taskdef name = "xjc" classname = "com.sun.tools.xjc.XJCTask">
   <classpath>
     <fileset refid = "tools"/>
   </classpath>
 </taskdef>
 
 <target name = "generate">
  <xjc schema = "src/main/xml/country.xsd" 
      destdir = "src/main/java" 
      package = "com.my.services.model"/>
 </target> 
</project>
 WSDL will have the schema and "wsimport" utility will generate the Classes for Marshaling and Un-Marshaling ( details of the wsimport utility can be found here http://ajaxrocks.blogspot.com/2013/08/jax-ws-call-ws-using-maven.html) . If you are generating using the WSDL there will be the WebService and PortType classes , delete them after generation .

Java Interface to Call WebService

Interface to call the webservice , it uses the generated classes . My case the class is GetCountryRequest and GetCountryResponse
package com.my.services.api;
import javax.xml.bind.annotation.XmlElement;
import io.spring.guides.gs_producing_web_service.GetCountryRequest;
import io.spring.guides.gs_producing_web_service.GetCountryResponse;
public interface MyService {
GetCountryResponse  countryRequest(GetCountryRequest request);
}

Stuff to Configure ( full xml shown below )

Gateway

Specify's the interface and map the method to the header 

<int:gateway id="myService" service-interface="com.my.services.api.MyService"
  default-request-channel="myService_routingChannel">
  <int:method name="countryRequest">
   <int:header name="REQUEST_TYPE" value="countryRequest" />
  </int:method>
 </int:gateway>

Publish-Subscribe Channel

Specify the Channel to map this way we can map the header value to the channel
<int:publish-subscribe-channel id="myService_routingChannel" />

Header-Value Router

Route the call based on the header value to the chain

 <int:header-value-router input-channel="myService_routingChannel" header-name="REQUEST_TYPE">
  <int:mapping value="countryRequest" channel="myService_countryRequestChannel" />
 </int:header-value-router>
 
 <int:channel id="myService_countryRequestChannel" />

Chain


Service-Activation -  transformer and the method to be called in the java class
Soap-Actionws:header-enricher ) - soap-action to be called ( you will find this in the wsdl ). Example this one is commented out as soap-action is empty . You need to provide this otherwise
outbound-gateway  -  WebService end point with message-sender  and unmarshaller

 <int:chain input-channel="myService_countryRequestChannel"
output-channel="myService_countryRequestOutputChannel" >
 
  <int:service-activator ref="MyWSTransformer"
   method="countryRequestSOAPRequest" />
 
  <!--  <ws:header-enricher>
   <ws:soap-action value="" />
  </ws:header-enricher> -->
 
  <ws:outbound-gateway
   uri="http://localhost:8080/ws"
   message-sender="MY.SOAP-MessageSender"  unmarshaller="myrequestJAXBUnMarshaller"/>
 </int:chain>
 

Outbound gateway

Configure the outbound gateway
  <ws:outbound-gateway

   uri="${my.purchase.url}"

   message-sender="MY.SOAP-MessageSender"  unmarshaller="mypurchaseJAXBUnMarshaller"/>

 </int:chain>

Authentication can also be added using
 <beans:bean id = "org.apache.commons.httpclient.Credentials-Service"
               class = "org.apache.commons.httpclient.UsernamePasswordCredentials"
               c:userName = "${userName}"
               c:password = "${password}"/>
and specify the ref as credentials-ref in the ws:outbound-gateway

Full XML


<?xml version = "1.0" encoding = "UTF-8"?>

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans  xmlns:int="http://www.springframework.org/schema/integration"
 xmlns:http="http://www.springframework.org/schema/integration/http"
 xmlns:ws="http://www.springframework.org/schema/integration/ws"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:beans="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:si-xml="http://www.springframework.org/schema/integration/xml"
  xmlns:oxm = "http://www.springframework.org/schema/oxm"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
  http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
     http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd
     http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd">
 

<!-- <beans:bean id="myManager"
     class="com.my.services.manager.MyServiceImpl" >
    <beans:property name="myService" ref="myService" />
 </beans:bean>
  -->
  <beans:bean id="MyWSTransformer"
     class="com.my.services.transformer.MyWSTransformer" />
 
 <beans:bean id="MY.SOAP-MessageSender"
  class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
  <beans:property name="connectionTimeout" value="3000" />
  <beans:property name="readTimeout" value="5000" />
 </beans:bean>
 
  <int:publish-subscribe-channel id="myService_routingChannel" />
  
 <int:gateway id="myService" service-interface="com.my.services.api.MyService"
  default-request-channel="myService_routingChannel">
  <int:method name="countryRequest">
   <int:header name="REQUEST_TYPE" value="countryRequest" />
  </int:method>
 </int:gateway>
  
 <int:header-value-router input-channel="myService_routingChannel" header-name="REQUEST_TYPE">
  <int:mapping value="countryRequest" channel="myService_countryRequestChannel" />
 </int:header-value-router>
 
 <int:channel id="myService_countryRequestChannel" />
 
 <int:chain input-channel="myService_countryRequestChannel"
output-channel="myService_countryRequestOutputChannel" >
 
  <int:service-activator ref="MyWSTransformer"
   method="countryRequestSOAPRequest" />
 
  <!--  <ws:header-enricher>
   <ws:soap-action value="" />
  </ws:header-enricher> -->
 
  <ws:outbound-gateway
   uri="http://localhost:8080/ws"
   message-sender="MY.SOAP-MessageSender"  unmarshaller="myrequestJAXBUnMarshaller"/>
 </int:chain>
 
 
   <si-xml:unmarshalling-transformer id="countryRequestUnmarshaller"
  input-channel="myService_countryRequestOutputChannel"
  unmarshaller="myrequestJAXBUnMarshaller" />
 
   <oxm:jaxb2-marshaller id = "myrequestJAXBUnMarshaller">
    <oxm:class-to-be-bound name = "io.spring.guides.gs_producing_web_service.GetCountryResponse"/>
   <oxm:class-to-be-bound name = "io.spring.guides.gs_producing_web_service.Country"/>
   <oxm:class-to-be-bound name = "io.spring.guides.gs_producing_web_service.Currency"/>
  <!--   <oxm:class-to-be-bound name = "io.spring.guides.gs_producing_web_service.GetCountryRequest"/> -->
  
 </oxm:jaxb2-marshaller>  
 
 
</beans:beans>

Java Transformer

Transformer has the method that makes the SOAP Request call , its clear if you look at the requestXML string.
GetCountryRequest is already marshaled and available as a input parameter . 

package com.my.services.transformer;



import java.text.SimpleDateFormat;
 
import java.util.Calendar;
 

import org.slf4j.Logger;
 
import org.slf4j.LoggerFactory;
 
import org.springframework.integration.annotation.Header;
import org.springframework.stereotype.Component;

import io.spring.guides.gs_producing_web_service.GetCountryRequest;
 
 
@Component
public class MyWSTransformer {
 
 static private final Logger logger = LoggerFactory.getLogger(MyWSTransformer.class);
 
 public static String countryRequestSOAPRequest(GetCountryRequest request)
 
 {
 
 logger.info("Calling MyWSTransformer with GetCountryRequest " +request.getName());
  String requestXml =" <gs:getCountryRequest xmlns:gs=\"http://spring.io/guides/gs-producing-web-service\">"+
         "  <gs:name>"+request.getName()+"</gs:name> "+
      " </gs:getCountryRequest>";
    
  return requestXml; 
 
 }
 
}