Accessing RPC/Encoded Web services in Servicemix / Fuse ESB

If there’s one issue I’ve found with ServiceMix, it’s that it tends to enthusiastically embrace a certain way of doing things while letting other methods fall to the wayside.  A prominent example of this is when constructing code to access web services.  By default, Servicemix uses Apache CXF as its web service layer.  CXF is a thoroughly modern framework for constructing web service clients and services, but because it’s so modern it doesn’t support some of the older web services definitions.  A particular example is ‘RPC/Encoded’ web services. If you’re not sure what an RPC/Encoded web service is, but you’ve run into the dreaded ‘rpc encoded wsdls are not supported in jaxws’ error message that is reported when you try to use CXF in this case, there’s a good (if rather dry) explanation of the various types of web services here but all you really need to know is that most people moved away from RPC/Encoded services a while ago, and most modern frameworks are dropping support for this style.  This is all well and good, but what should you do if you need to access a piece of 3rd party software using this style of web service?  CXF doesn’t support it at all, and servicemix doesn’t offer an alternative web framework that does support it.  If you google this issue, you find a load of posts form mailing groups and forums discussing it, but none offering a real solution.

Well, as with most things relating to servicemix there’s a couple of ways of approaching this.

The first, and most time consuming is to simply construct XML Requests and / or responses yourself and then slot your data in before sending it off to the remote endpoint using CXF.  SoapUI is useful here to work out what your XML should look like, and you can use the camel-freemarker component to add in your data and make requests.  For responses, you need some way of parsing the xml that comes back and extracting the relevant data.  If you only have a few requests / responses to make, this is probably a good method.  Once you start adding more requests / responses though it very quickly becomes unwieldy and time consuming to write the parsers and request xml templates for this.

So, what’s the better way of doing this?  Well, before CXF came along there was another Java based web services framework called Axis.  And, fortunately for us, Axis does support RCP/Encoded web services.  However, there are some issues with getting Axis support into servicemix.  Firstly, it’s not supported at all.  There’s no nice FuseSource provided packages available and, because Axis makes use of some of the same wiring as CXF, having Axis present in your OSGI environment can cause all kinds of issues with packages using Axis dependencies instead of CXF dependencies.

Luckily for you, I’ve already done the work to get this working, and All you need to do is copy the methodology below for a nice working RCP/Encoded client API.

For ease of use, I’ve split my project into two separate areas.  A ‘generator’ bundle, which produces our Axis API code, and an ‘API’ bundle which makes use of that code.

Here’s the POM.xml for the Generator bundle:

<?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/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.tall-paul</groupId>
 <artifactId>my-rpc-generator</artifactId>
 <packaging>pom</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>${project.artifactId}</name>
 <properties>
 <camel-version>2.9.0</camel-version>
 <activemq-camel-version>5.5.1</activemq-camel-version>
 <servicemix-version>2011.02</servicemix-version>
 <maven-bundle-plugin-version>2.3.4</maven-bundle-plugin-version>
 <build-plugins-plugin-version>2.5</build-plugins-plugin-version>
 <build-helper-plugin-version>1.7</build-helper-plugin-version>
 <maven-resources-plugin-version>2.5</maven-resources-plugin-version>
 <maven-compiler-plugin-version>2.3.2</maven-compiler-plugin-version>
 <osgi-import-package>*,org.apache.camel.osgi</osgi-import-package>
 <osgi-export-package/>
 </properties>
 <dependencies>
 <dependency>
 <groupId>org.apache.axis</groupId>
 <artifactId>axis</artifactId>
 <version>1.4</version>
 </dependency>
 <dependency>
 <groupId>org.apache.axis</groupId>
 <artifactId>axis-jaxrpc</artifactId>
 <version>1.4</version>
 </dependency>
 <dependency>
 <groupId>org.apache.axis</groupId>
 <artifactId>axis-saaj</artifactId>
 <version>1.4</version>
 </dependency>
 <dependency>
 <groupId>axis</groupId>
 <artifactId>axis-wsdl4j</artifactId>
 <version>1.5.1</version>
 </dependency>
 <dependency>
 <groupId>commons-discovery</groupId>
 <artifactId>commons-discovery</artifactId>
 <version>0.4</version>
 </dependency>
 <dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.14</version>
 </dependency>
 </dependencies>
 <build>
 <pluginManagement>
 <plugins>
 <!-- used to generate the MANIFEST-FILE of a bundle -->
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <configuration>
 <source>1.5</source>
 <target>1.5</target>
 </configuration>
 </plugin>
 <plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>axistools-maven-plugin</artifactId>
 <configuration>
 <sourceDirectory>src/main/resources/</sourceDirectory>
 <packageSpace>my.package.namespace</packageSpace>
 <testCases>false</testCases>
 <serverSide>true</serverSide>
 <subPackageByFileName>false</subPackageByFileName>
 </configuration>
 <executions>
 <execution>
 <goals>
 <goal>wsdl2java</goal>
 </goals>
 </execution>
 </executions>
 </plugin>
 </plugins>
 </pluginManagement>
 </build>
</project>

There’s a couple of things you’ll need to change here for your project.  As well as the usual maven artifactID etc, you’ll need to change the ‘packageSpace’ element to reflect what package you want the generated code to fall under.  Your RPC/Encoded wsdl needs to go in your src/main/resources directory.  Do a maven build, and you should find a load of generated code in your target directory (yay!).  Now we have to actually do something with that code.  Here, I copy the generated code into my ‘Api’ project, the pom for which looks like this:

<?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>
  <groupId>com.tall-paul</groupId>
  <artifactId>my-rpc-api</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>bundle</packaging>
  <name>my-rpc-api</name>
  <description>My RPC Api</description>
  <properties>
    <camel-version>2.9.0</camel-version>
    <activemq-camel-version>5.5.1</activemq-camel-version>
    <servicemix-version>2011.02</servicemix-version>
    <maven-bundle-plugin-version>2.3.4</maven-bundle-plugin-version>
    <build-plugins-plugin-version>2.5</build-plugins-plugin-version>
    <build-helper-plugin-version>1.7</build-helper-plugin-version>
    <maven-resources-plugin-version>2.5</maven-resources-plugin-version>
    <maven-compiler-plugin-version>2.3.2</maven-compiler-plugin-version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.apache.axis</groupId>
      <artifactId>axis</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis</groupId>
      <artifactId>axis-jaxrpc</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.axis</groupId>
      <artifactId>axis-saaj</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>javax.xml</groupId>
      <artifactId>jaxrpc-api-osgi</artifactId>
      <version>1.1-b01</version>
    </dependency>
    <dependency>
      <groupId>axis</groupId>
      <artifactId>axis-wsdl4j</artifactId>
      <version>1.5.1</version>
    </dependency>
    <dependency>
      <groupId>commons-discovery</groupId>
      <artifactId>commons-discovery</artifactId>
      <version>0.4</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.0.2</version>
    </dependency>
    <dependency>
      <groupId>javax.xml</groupId>
      <artifactId>jaxrpc-api</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>   
  </dependencies>
  <build>
    <defaultGoal>install</defaultGoal>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <version>${maven-bundle-plugin-version}</version>
        <extensions>true</extensions>
        <configuration>
          <instructions>
            <Bundle-SymbolicName>TESTING</Bundle-SymbolicName>
            <Export-Package>my-package-namespace,javax.xml.rpc</Export-Package>
            <Embed-Dependency>
							axis;scope=compile|runtime,
							axis-jaxrpc;scope=compile|runtime,
							axis-saaj;scope=compile|runtime,
							jaxrpc-api-osgi;
							scope=compile|runtime,
							axis-wsdl4j;scope=compile|runtime,
							commons-discovery;scope=compile|runtime,
							jaxrpc-api;scope=compile|runtime,
							log4j;scope=compile|runtime,
							commons-logging;scope=compile|runtime
						</Embed-Dependency>
            <Embed-Transitive>true</Embed-Transitive>
            <Bundle-ClassPath>.,{maven-dependencies}</Bundle-ClassPath>
            <Import-Package>
							*;resolution:=optional,
							com.sun.jdmk.comm;resolution:=optional,
							com.sun.jimi.core;resolution:=optional,
							com.sun.net.ssl;resolution:=optional,
							com.sun.net.ssl.internal.ssl;resolution:=optional,
							org.apache.bsf;resolution:=optional,
							org.apache.log;resolution:=optional,
							sun.awt.image.codec;resolution:=optional,
							sun.security.provider;resolution:=optional,
							org.exolab.castor.xml;resolution:=optional,
							org.apache.camel.osgi,
							</Import-Package>
          </instructions>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin-version}</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

 

The real magic here is the ‘Embed-Depency’ element, which ensures the Jars referenced here are downloaded and bundled into our project, rather than being imported from the OSGI container at runtime.

And that’s pretty much it, you can now use the generated axis code in much the same way as similar CXF code.




5 Comments

Thank you for this post! Really useful!

One problem i had with this part:

*;resolution:=optional,
com.sun.jdmk.comm;resolution:=optional,
com.sun.jimi.core;resolution:=optional,
com.sun.net.ssl;resolution:=optional,
com.sun.net.ssl.internal.ssl;resolution:=optional,
org.apache.bsf;resolution:=optional,
org.apache.log;resolution:=optional,
sun.awt.image.codec;resolution:=optional,
sun.security.provider;resolution:=optional,
org.exolab.castor.xml;resolution:=optional,
org.apache.camel.osgi,

Were duplicating imports. I had to move *;resolution:=optional to the end of block.

Leave a Reply

Your email address will not be published. Required fields are marked *