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);