Monday, June 20, 2011

Writing Integration tests for Spring WS endpoints

Writing Integration tests for Spring WS endpoints is easy, based on this resource from Spring-WS reference site. Spring WS provides a MockWebServiceClient class to test the Spring-WS endpoints.

My endpoint has the following signature:


@Endpoint
public class GetMemberDetailsEndpoint {

 @Resource private MemberManager memberManager;

 @PayloadRoot(namespace = "http://bk.org/memberservice/", localPart = "MemberDetailsRequest")
 @ResponsePayload
 public MemberDetailsResponse getMemberDetails(@RequestPayload MemberDetailsRequest request) throws Exception {
  MemberDetail memberDetail = memberManager.findByMemberId(request.getId());
                ......
 }

I have a memberManager bean dependency in my endpoint, I mock this up using easymock first:
<bean name="memberManager" class="org.easymock.EasyMock" factory-method="createMock">
  <constructor-arg value="org.bk.memberservice.service.MemberManager"/>
 </bean>

this way Spring will wire it to the endpoint when starting up the bean factory. Record the easymock with appropriate expectations:
MemberDetail memberDetail = new MemberDetail("john doe", "111-111-1111", "City", "State");
        memberDetail.setId(1L);
        expect(memberManager.findByMemberId(1L)).andReturn(memberDetail);
        replay(memberManager);

Initialize MockWebserviceClient, set up the test:
mockClient = MockWebServiceClient.createClient(applicationContext);
        Source requestPayload = new StringSource(
                "<mem:MemberDetailsRequest xmlns:mem=\"http://bk.org/memberservice/\">"
                        + "<mem:id>1</mem:id>" 
                        + "</mem:MemberDetailsRequest>");
        Source responsePayload = new StringSource(
                "<ns3:MemberDetailsResponse xmlns:ns3=\"http://bk.org/memberservice/\">"
          + "<memberDetail>" 
          + "<id>1</id>"
          + "<name>john doe</name>"
          + "<phone>111-111-1111</phone>"
          + "<city>City</city>"
          + "<state>State</state>"
          + "</memberDetail>"
      + "</ns3:MemberDetailsResponse>");

        mockClient.sendRequest(withPayload(requestPayload)).andExpect(payload(responsePayload));
        verify(this.memberManager);
This completes the test, MockWebserviceClient would take care of packaging up the raw xml request, dispatching it the appropriate WS endpoint, getting the response and validating it. Updated codesample with integration test available at: git://github.com/bijukunjummen/memberservice-contractfirst.git

Saturday, June 18, 2011

Supporting Spring-WS and Spring MVC integration in a project

Spring WS and Spring MVC provide different front controller implementations as a gateway to the webservice and the MVC functionality respectively. The Dispatcher Servlet used by Spring-WS is :

org.springframework.ws.transport.http.MessageDispatcherServlet
and the one used by Spring MVC is :
org.springframework.web.servlet.DispatcherServlet
To have a combined Spring MVC and Spring-WS project, it is possible to configure these front controllers based on the URI pattern of the request, in the following way:
<servlet>
        <servlet-name>member-ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/META-INF/spring/applicationContext-ws.xml</param-value>
        </init-param>
    </servlet>
    
    <servlet>
        <servlet-name>member-web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
        </init-param>
    </servlet>    

    <servlet-mapping>
        <servlet-name>member-ws</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>member-ws</servlet-name>
        <url-pattern>*.wsdl</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>member-web</servlet-name>
        <url-pattern>/web/*</url-pattern>
    </servlet-mapping>    


In this specific instance, all requests to /web/ is handled by the Spring MVC DispatcherServlet whereas all requests to /services is handled by Spring-WS DispatcherServlet. Further, each dispatcher servlet is configured with its custom Spring configuration file, the one for Spring MVC loads up the contollers, the one for Spring WS loads up the Webservice endpoints.

I am not sure if this is a optimal configuration, but it works for me in this project available at: git://github.com/bijukunjummen/memberservice-contractfirst.git


OR An alternate way to hook up DispatcherServlet to handle Spring-WS requests is described here!: http://static.springsource.org/spring-ws/sites/2.0/reference/html/server.html#d4e884

Sunday, June 12, 2011

IntelliJ IDEA for Scala/SBT projects

I have found it easier to use Intellij IDEA with Scala plugins, for Scala learnings. This is the way I normally spin up a scala project.

1. Use sbt to first create a shell of a project:
D:\learn\shell-project>sbt

D:\learn\shell-project>set SCRIPT_DIR=C:\util\sbt\

D:\learn\shell-project>java -Xmx512M -jar "C:\util\sbt\sbt-launch-0.7.5.jar"
Project does not exist, create new project? (y/N/s) y
Name: shellproject
Organization: org.bk
......

2. Add eclipsify plugin - this is done by creating a sbt ProjectDefinition under project/plugins folder:
import sbt._

 class MySbtProjectPlugins(info: ProjectInfo) extends PluginDefinition(info) {
       lazy val eclipse = "de.element34" % "sbt-eclipsify" % "0.7.0"
 }


3. Add scalatest to the dependency - this is done by creating a file of the following type under project/build folder:

import sbt._
import de.element34.sbteclipsify._

class MySbtProject(info: ProjectInfo) extends DefaultProject(info) with Eclipsify {
    override def libraryDependencies = Set(
       "org.scalatest" % "scalatest" % "1.3" % "test->default"
    ) ++ super.libraryDependencies
}

4. reload sbt(run "reload" in the sbt console)

5. A new action "eclipse" should now be available. Run "eclipse"

6. That's it. Now a new project should be available for IntelliJ idea to import as a project.

Monday, June 6, 2011

Lucene Search on Numeric Long field

Something new to me, I have previously enabled Lucene search using pure text fields, but stumbled recently when trying to search using a Long field:
IndexWriterConfig indexConfig = new IndexWriterConfig(Version.LUCENE_30,  new StandardAnalyzer(Version.LUCENE_30));
IndexWriter indexWriter = new IndexWriter(directory, indexConfig );

Document doc = new Document();
doc.add(new NumericField("id", Store.YES, true).setLongValue(123L));
and to search on this field:
IndexSearcher is = new IndexSearcher(dir);
Query query = new TermQuery(new Term("id", NumericUtils.longToPrefixCoded(123L)));
TopDocs hits = is.search(query, 10);

Tuesday, May 31, 2011

Tower of Hanoi in Scala

Had a little fun with scala after 2 months, an implementation of Tower of Hanoi:
object Hanoi{
    def move(n:Int, fromTower:String, toTower:String, usingTower:String):Unit = {
        if (n==0) return
        move(n-1, fromTower, usingTower, toTower);
        println("Moving from " + fromTower + " to " + toTower)
        move(n-1, usingTower, toTower, fromTower);
    }
}

Thursday, May 26, 2011

Drools session at Indianapolis Java User Group

I attended a JBoss Drools session at the Indianapolis JUG yesterday evening. The session was presented by Ray Ploski, a Principal Solution Architect with Redhat.
Ray went over the breadth of product offerings within the Drools Umbrella -

  • Drools Expert(the Rule engine)
  • Drools Guvnor(Rules Hosting/Management)
  • Drools Fusion(Complex Event Processing) 
  • Drools Planner(Planning)
  • Drools Flow(Process Flow)
All these product offerings is together referred to as the "Drools Business Logic Integration Platform". 
I had gone into the meeting with the mistaken assumption that Drools is just a rule engine and came out  a little wiser. 

Some notes that I have from the meeting are the following:
  • Ray has hosted the presentation, samples at this location - http://bit.ly/cjug-drools-2011
  • Drools Flow is being merged into jBPM5 - with support for Graphical process flows tightly integrated with Drools Expert.
  • Rules and other artifacts can be versioned within Guvnor - internally it uses JCR as the API for versioning with Jack Rabbit as the implementation
  • Java code can implement a pull based model, to pull in rules from Guvnor - at a specified schedule pull in the latest rules from Guvnor, thus reflecting the rule changes without needing to restart application
  • Drools Expert uses a MVEL dialect to express the condition(LHS) part of the rule
  • One of the largest Drools users has Million and a half rules defined!
PS: E-gineering hosted this meeting at their great new office location.

Wednesday, May 25, 2011

java.lang.ClassCastException: org.apache.cxf.transport.servlet.CXFServlet cannot be cast to javax.servlet.Servlet

I was getting this exception:
java.lang.ClassCastException: org.apache.cxf.transport.servlet.CXFServlet cannot be cast to javax.servlet.Servlet
        at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1116)
        at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
        at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4350)
        at org.apache.catalina.core.StandardContext.start(StandardContext.java:4659)
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
        at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
when trying to start up Tomcat using maven command -
mvn tomcat:run
on this application -
git://github.com/bijukunjummen/memberservice-codefirst.git
It turns out that the issue is related to a dependency that CXF pulls in which conflicts with Tomcats version of Servlet class. The fix is to change the maven dependency from:
  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>apache-cxf</artifactId>
   <version>${cxf.version}</version>
   <type>pom</type>
  </dependency>
to the specific set of CXF libs:
  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-frontend-jaxws</artifactId>
   <version>${cxf.version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-transports-http</artifactId>
   <version>${cxf.version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-transports-http</artifactId>
   <version>${cxf.version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-ws-security</artifactId>
   <version>${cxf.version}</version>
  </dependency>
  <dependency>
      <groupId>org.apache.ws.security</groupId>
      <artifactId>wss4j</artifactId>
      <version>1.6.0</version>
  </dependency> 
Now
mvn tomcat:run
should run through just fine.