Building opensource web services using JAX-WS

Posted by dave
on April 03, 2007 @ 12:17 PM

Over on SOS Adrian has posted a set of slides that describe how to build multi-protocol, opensource, web services using JAX-WS.

The presentation gives a great overview of the architecture that allows Celtix Enterprise to deploy services that simulateneosly support multiple payloads (SOAP, XML, JSON) and transports (HTTP, JMS, AMQP). It also give some very helpful pointers on JAX-WS development in general - well worth checking it out.

Wire Level Debug for ActiveResource

Posted by dave
on February 13, 2007 @ 08:08 AM

ActiveResource provides the mechanism to implement RESTful Ruby clients in Rails 1.2. If you want to see wire level debug output from the Net::HTTP layer you can enable it by editing:


[activeresource]/lib/activeresource/connection.rb

Add the set_debug_output line to the (private) http method.

1
2
3
4
5
6
7
8
9
10
11
12

     def http
        unless @http
          @http             = Net::HTTP.new(@site.host, @site.port)
          @http.use_ssl     = @site.is_a?(URI::HTTPS)
          @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @http.use_ssl
          # enable debug http wirelevel dumps
          @http.set_debug_output $stderr
        end

        @http
      end
Very handy for debugging ActiveResource with a Rails console.

Web of Services Workshop 1

Posted by dave
on January 31, 2007 @ 03:54 AM

Over on Eric's blog he writes about the upcoming W3C Web of Services Workshop. The overall theme of the workshop is to discuss the convergence of RESTful and traditional Web Services - quite the hot topic! The full program is available here.

Specifying a custom soap header in .NET 0

Posted by admin
on December 17, 2006 @ 02:31 PM

Today I was doing some interop testing between a .NET client and a backend Artix* SOAP/HTTP server that requires a custom soap header [* I work for IONA].

Defining the header via wsdl

Theres a couple of different ways to define the soap header. In this case I defined the type for the custom soap header explicitly in the web service contract.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


   <types>
  ...      
        <complexType name="SOAPHeaderData">
           <sequence>
             <element name="originator" type="xsd:string"/>
                <element name="message" type="xsd:string"/>
           </sequence>
        </complexType>
        <element name="SOAPHeaderInfo" type="tns:SOAPHeaderData"/>
        ...
   </types>

A message part was defined for the SOAPHeaderInfo element.

1
2
3
4

 <message name="header_message">
        <part element="tns:SOAPHeaderInfo" name="header_info"/>
    </message>

The header was added to the binding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

   <binding name="Greeter_SOAPBinding" type="tns:Greeter">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="sayHi">
            <soap:operation soapAction="" style="document"/>
            <input name="sayHiRequest">
                <soap:body use="literal"/>
                <soap:header message="tns:header_message" part="header_info" 
                    use="literal"/>
            </input>
            <output name="sayHiResponse">
                <soap:body use="literal"/>
                <soap:header message="tns:header_message" part="header_info" 
                    use="literal"/>
            </output>
        </operation>
   </binding>

.NET client

On the client side I started with a boilerplate C# console application.

In order to populate client requests with the custom header and invoke on the server:
1.In the Visual Studio project navigator, right click to add a "Web Reference"
2.In the WebReference wizard, specify a file path to the wsdl and click "Go".
3.The wizard should detect the methods in your wsdl. Specify a name for the proxy and click finish.
4.In the code, as we added the header to the binding, .NET creates a placeholder where you can set the header.
5.I set the header as shown in the following code:
1
2
3
4
5
6
7
8
9
10


SOAPHeader.SOAPService service = new   SOAPHeader.SOAPService(); 
SOAPHeader.SOAPHeaderData header = new  SOAPHeader.SOAPHeaderData();
    
 header.message="test soap header message";
 header.originator="dave.stanley";
 service.SOAPHeaderInfo = header;
 service.greetMe("test");

The message that goes over the wire looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


<?xml version='1.0' encoding='utf-8'?>
<SOAP-ENV:Envelope 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:m1="http://www.iona.com/soap_header" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
 <SOAP-ENV:Header>   
  <m1:SOAPHeaderInfo SOAP-ENV:mustUnderstand="1">
      <originator>dave.stanley</originator>   
      <message>test soap header message</message>
  </m1:SOAPHeaderInfo>
 </SOAP-ENV:Header>
 <SOAP-ENV:Body>
  <m1:requestType>me</m1:requestType>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Next is the https interop ...

Getting started with Apache CXF

Posted by admin
on October 27, 2006 @ 05:58 PM

Today I setup CXF, a new services framework project currently under incubation at Apache. CXF is the merging of the Objectweb Celtix project and the Codehaus XFire project. Its still early days for the project but if you check out the project goals you can get a good idea of the featureset that CXF will eventually offer. Today I just wanted to get as far as running a couple of the demos.

Installing:

CXF has external dependencies on JDK 1.5 and Maven 2.x. As there is no official release yet you need the build the project directly from the subversion repository. If you want to build the samples you will also need ant installed.

1. Install JDK 1.5 (approx 130MB)

The Sun download page has three different distributions, JDK with NetBeans IDE, JDK with JEE and just the straight 1.5 JDK. I just opted for your basic 1.5 JDK.

2. Install Maven 2.0.4(approx 1.15MB)

The Maven install is just a zip/gz file. You just need to download the zip and unzip it on your system. Then you need to add the /bin directory to your path.

>cd /x1/maven2.0.4
>wget http://www.apache.org/dist/maven/binaries/maven-2.0.4-bin.tar.gz 
>tar -zxvf maven-2.0.4-bin.tar.gz
>export PATH=/x1/maven2.0.4/bin:$PATH

On windows download the .zip version from the link above and use Winzip to unpack the zip. Then set the path as shown here.
>set Path=\bin;%Path%

3. Install subversion windows or unix (approx 11MB).

The windows link is to the subversion installer gui. This version of subversion uses BerkeleyDB 4.3. If you have any other applications on your system that might use a different version of BerkeleyDB (e.g Orbix), keep this in mind as you may see dll conflicts between the two versions. Once installed you should be able to run 'svn ?' from a command prompt and get a list of available svn commands.

4. If necessary install ant

Install is similar to maven, download and unzip to your system. Then setup the paths as shown:

>set ANT_HOME=c:\dev\ant
>set PATH=%PATH%;%ANT_HOME%\bin
>export ANT_HOME=/x1/ant
>export PATH=${PATH}:${ANT_HOME}/bin

Checking out the CXF source code:

To checkout the CXF source run :
>cd /x1/cxf
>svn co http://svn.apache.org/repos/asf/incubator/cxf/trunk

This is a good point to go and make yourself a cup of tea as this and the next step take some time.

Building the source code:

Once the source code is checked out, setup your environment so that java 1.5 is in your path. If you get this wrong you will see an error similar to this when you try and build with a JDK < JDK 1.5.

INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

Failure executing javac, but could not parse the error:
javac: invalid target release: 1.5

I also set things up such that my /bin directory was in the path. To build the project run:

>cd /x1/cxf/trunk
>mvn install

The first time you build CXF, maven will download what seems like an endless amount of dependency jars to a local maven repository on your machine. On windows this is below <> and on linux this was below <>. I have seen periodic download failures where maven fails to download a dependency but re-running mvn install tended to work the second time around.

Another thing to watch out for on windows is that there's a fair bit of shuffling going on in the CXF codebase right now. If the case of a file is renamed, svn on windows cannot detect this and this will leave you scratching your head as to why the project won't build. If you end up with a compile error that doesn't make sense try a 'mvn clean' and then a 'mvn install' and see if that fixes it.

Building the distribution snapshot:

Once the build completes you need to cd into the /distribution directory and run 'maven install' a second time to build a distributable version of cxf. This will produce a distribution with a samples directory which includes the 'hello_world' demo that we want to run.

>cd /x1/cxf/trunk/distribution
>mvn install
Installing the distribution snapshot:

The distribution build produces two distribution files (cxf-2.0-incubator-M1-SNAPSHOT.tar.gz and cxf-2.0-incubator-M1-SNAPSHOT.zip). Take the appropriate distribution and unzip it in a separate directory to the checked out code, for example:
>cp /x1/cxf/trunk/distribution/target/cxf-2.0-incubator-M1-SNAPSHOT.tar.gz /x1/cxf-distro/
>cd /x1/cxf-distro/
>tar -zxvf cxf-2.0-incubator-M1-SNAPSHOT.tar.gz

Now you have something you can test with.


Running the hello world demo:
>cd /x1/cxf-distro/cxf-2.0-incubator-M1-SNAPSHOT/samples/hello_world
>ant
>ant -projecthelp
Buildfile: build.xml

Main targets:

 build           build demo client and server
 client          run demo client
 client-servlet  run demo client hitting servlet
 server          run demo server
Default target: build
Now we run the server and the client
>ant server&

server:
     [java] Starting Server
     [java] Server ready...

>ant client

client:
     [java] Invoking sayHi...
     [java] Server responded with: Bonjour

     [java] Invoking greetMe...
     [java] Server responded with: Hello dstanley

     [java] Invoking greetMe with invalid length string, expecting exception...

     [java] Invoking greetMeOneWay...
     [java] No response from server as method is OneWay

     [java] Invoking pingMe, expecting exception...
     [java] Expected exception: PingMeFault has occurred: PingMeFault raised by server
     [java] FaultDetail major:2
     [java] FaultDetail minor:1
Enabling Logging:

So after running the demo, it looked like everything worked but there was not much to see. The next step for me was to enable some logging and get a peek at whats happening under the covers.

>cd /x1/cxf-distro/cxf-2.0-incubator-M1-SNAPSHOT/etc
>emacs logging.properties
See here for more details on configurable logging levels. I had to set both '.level' and 'java.util.logging.ConsoleHandler.level' in my logging.properties file to get logging output in my console.
.level= INFO
java.util.logging.ConsoleHandler.level = INFO 

I had to set the level to FINEST (very verbose!) to see the message going over the wire. Would be nice if this was logged at INFO level.

Thats about it for this time. Hope this article helps. If you spot any problems feel free to point them out.

Invoking backend SOAP/HTTP Web Services with Ruby

Posted by dave
on October 06, 2006 @ 10:54 AM
So ya wanna invoke on a backend SOAP/HTTP Web Service from Rails do ye? SOAP4R (SOAP 4 Ruby) gets the job done as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'soap/wsdlDriver'
URL = 'http://localhost/hello_world.wsdl'

factory = SOAP::WSDLDriverFactory.new(URL)
driver = factory.create_rpc_driver
driver.wiredump_dev =  STDERR

begin
 
 // Invoke on the webservice here
 result = driver.sayHi
 result = driver.greetMe("dave")
 puts result

rescue Exception => e

  #Handle SOAP Faults here

end
This code snippet uses SOAP4R 1.5.5. This is a somewhat simplified example as theres only two methods defined in the wsdl, sayHi takes 0 parameters and greetMe just takes a string. Needless to say, this can be placed inside your Ruby on Rails controller too. If you have complextypes it looks as though you need to use wsdl2ruby to generate helper classes for the complextypes. I might be doing something wrong, but this version of SOAP4R looks to have some problems handling one particular rpc/literal wsdl I have here, but a bit more digging is required.