<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Olaf&#039;s blog &#187; Spring-Remoting</title>
	<atom:link href="http://olafsblog.sysbsb.de/tag/spring-remoting/feed/" rel="self" type="application/rss+xml" />
	<link>http://olafsblog.sysbsb.de</link>
	<description>Olaf&#039;s blog on software development and life</description>
	<lastBuildDate>Sat, 14 Aug 2010 20:39:43 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Using RPC-style, encoded Axis 1 webservices with spring-remoting</title>
		<link>http://olafsblog.sysbsb.de/using-rpc-style-encoded-axis-1-webservices-with-spring-remoting/</link>
		<comments>http://olafsblog.sysbsb.de/using-rpc-style-encoded-axis-1-webservices-with-spring-remoting/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 20:16:55 +0000</pubDate>
		<dc:creator>olaf</dc:creator>
				<category><![CDATA[J2EE]]></category>
		<category><![CDATA[System architecture]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[Axis 1]]></category>
		<category><![CDATA[Jax-ws]]></category>
		<category><![CDATA[Spring-Remoting]]></category>

		<guid isPermaLink="false">http://olafsblog.sysbsb.de/?p=76</guid>
		<description><![CDATA[Preamble
When working with enterprise integration you will quite often deal with legacy systems integration.
I recently had the case where integration of webservices was requested, and much to my surprise there are huge issues when attempting to integrate old webservices (as in pre axis2 and pre jax-ws).
These webservices use outdated or unsupported methods, such as rpc-style/encoded [...]]]></description>
			<content:encoded><![CDATA[<h2>Preamble</h2>
<p>When working with enterprise integration you will quite often deal with legacy systems integration.</p>
<p>I recently had the case where integration of webservices was requested, and much to my surprise there are huge issues when attempting to integrate old webservices (as in pre axis2 and pre jax-ws).</p>
<p>These webservices use outdated or unsupported methods, such as <a href="http://ws.apache.org/axis2/1_2/Axis2-rpc-support.html">rpc-style/encoded</a> communication, or even worce, outdated elements in the XML messages, for instance &lt;multiref&gt;&#8217;s. Having to integrate such services does of course imply that you cannot change anything on the server side, and thus such an outdated format must still be consumed.<br />
This is a huge problem, since current WS implementations, such as the popular axis2 framework, do simply not support these formats.</p>
<p>What surprised me the most about this is that webservices are designed to make systems independent by defining a common communication and data format, which is the exact opposite of what is going on here &#8211; and these formats are not that old, we are probably talking 4-5 years.</p>
<p>Developers facing these problems are taking rather desperate measures to work around these problems, such as <a href="http://www.jroller.com/0xcafebabe/entry/migrating_smoothly_from_rpc_encoded">using on-the-fly XSLT transformation to convert incoming and outgoing WS messages</a>. However, this binds your application even stronger to the outdated format and specific service data.</p>
<p>In my case I wanted to use <a href="http://static.springsource.org/spring/docs/2.5.x/reference/remoting.html">Spring-remoting</a>. Since the service was RPC-style, I wanted to use the <a href="http://static.springsource.org/spring/docs/2.5.6/api/org/springframework/remoting/jaxrpc/JaxRpcPortProxyFactoryBean.html">JaxRpcPortProxyFactoryBean</a> to create an on-the-fly proxy implementing the service interface. What I did not want to do is having to write any additional code to consume the service. Furthermore, I wanted the complex objects transferred by the services to be represented by standard JAVA beans, and not be generated using the wsdl2java axis1 tools. Here is how I got this to work.<br />
<span id="more-76"></span></p>
<h2>Using Spring-remoting&#8217;s jax-rpc support with axis 1</h2>
<h3>Required maven dependencies</h3>
<p>First, I was willing to make the compromise of having axis1 on my classpath. This might not be the way to go for everyone, unless you are using an OSGI framework, which was not an option in my case. Using jax-rpc with axis1 from spring requires the following dependencies (I am using <a href="http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html">maven&#8217;s dependency management</a> for the sake of simplicity):</p>
<pre class="brush: xml;">
&lt;dependency&gt;
  &lt;groupId&gt;commons-io&lt;/groupId&gt;
  &lt;artifactId&gt;commons-io&lt;/artifactId&gt;
  &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;commons-discovery&lt;/groupId&gt;
  &lt;artifactId&gt;commons-discovery&lt;/artifactId&gt;
  &lt;version&gt;0.4&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.apache.axis&lt;/groupId&gt;
  &lt;artifactId&gt;axis-jaxrpc&lt;/artifactId&gt;
  &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.apache.axis&lt;/groupId&gt;
  &lt;artifactId&gt;axis&lt;/artifactId&gt;
  &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.apache.axis&lt;/groupId&gt;
  &lt;artifactId&gt;axis-saaj&lt;/artifactId&gt;
  &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;wsdl4j&lt;/groupId&gt;
  &lt;artifactId&gt;wsdl4j&lt;/artifactId&gt;
  &lt;version&gt;1.6.2&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.springframework&lt;/groupId&gt;
  &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;
  &lt;version&gt;2.5.6&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;org.springframework&lt;/groupId&gt;
  &lt;artifactId&gt;spring-web&lt;/artifactId&gt;
  &lt;version&gt;2.5.6&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
  &lt;groupId&gt;cglib&lt;/groupId&gt;
  &lt;artifactId&gt;cglib-nodep&lt;/artifactId&gt;
  &lt;version&gt;2.2&lt;/version&gt;
&lt;/dependency&gt;
</pre>
<p>Note that this setup is for JDK 1.5. JDK 1.6 ships with quite a lot of jax-ws libraries, so you might want to check whether all of these dependencies are still required when using 1.6, but I fear they might.</p>
<h3>Configuring the service using the port proxy factory bean</h3>
<p>First, I downloaded the WSDL&#8217;s from the services and placed them within my src/main/resources folder to use them offline. This is not required, but I do recommend it. Next step was to set up the factory beans for jax-rpc to generate implementations of my service interfaces:</p>
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
  xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
  xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&quot;&gt;

  &lt;bean id=&quot;myWebService&quot; class=&quot;org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean&quot;&gt;
    &lt;property name=&quot;serviceInterface&quot; value=&quot;my.package.MYServiceInterface&quot; /&gt;
    &lt;property name=&quot;wsdlDocumentUrl&quot; value=&quot;classpath:path/to/my/wsdl.swdl&quot; /&gt;
    &lt;property name=&quot;namespaceUri&quot; value=&quot;namespace/as/defined/in/wsdl&quot; /&gt;
    &lt;property name=&quot;serviceName&quot; value=&quot;ServiceNameFromWsdl&quot; /&gt;
    &lt;property name=&quot;portName&quot; value=&quot;PortNameFromWsdl&quot; /&gt;
    &lt;property name=&quot;servicePostProcessors&quot;&gt;
      &lt;list&gt;
        &lt;!--
          This is for mapping your POJO's to the complex service objects (if you are using any).
          You can use an arbitrary number of mapping &lt;bean&gt; configurations for every model
          namespace of your WSDL.
        --&gt;
        &lt;bean
          class=&quot;org.springframework.remoting.jaxrpc.support.AxisBeanMappingServicePostProcessor&quot;&gt;
	  &lt;!-- encoding style as in WSDL, in my case the outdated soap/encoding --&gt;
          &lt;property name=&quot;encodingStyleUri&quot; value=&quot;http://schemas.xmlsoap.org/soap/encoding/&quot; /&gt;
          &lt;property name=&quot;typeNamespaceUri&quot; value=&quot;namespace/for/models/as/defined/in/wsdl&quot; /&gt;
          &lt;property name=&quot;beanMappings&quot;&gt;
            &lt;props&gt;
              &lt;prop key=&quot;my.package.MyPojo&quot;&gt;ModelNameInWsdl&lt;/prop&gt;
            &lt;/props&gt;
          &lt;/property&gt;
        &lt;/bean&gt;
      &lt;/list&gt;
    &lt;/property&gt;
  &lt;/bean&gt;
&lt;/beans&gt;
</pre>
<p>If the service specifies an operation such as <code>ComplexResponseVO getResults(ComplexRequestVO)</code>, you would thus define an interface like so:</p>
<pre class="brush: java;">
public interface MyService {
     MyResponsePojo getResults(MyRequestPojo);
}
</pre>
<p>And map the <code>MyResponsePojo</code> and <code>MyRequestPojo</code> using the AxisBeanMappingServicePostProcessor in the above spring configuration. Spring will serialize and deserialize the pojos using standard getter and setter methods to obtain the values, thus you do not have to care for serialization or marshalling. In fact, your pojos do not even have to be Serializable.</p>
<p>Once you have this stuff up and running, you can inject the <code>MyService</code> implementation into any other spring bean and use it like any other bean. Spring will convert the usually annoying and unrecoverable RemoteExceptions into unchecked remote exceptions, thus if you want to handle these, catching <a href="http://static.springsource.org/spring/docs/2.5.6/api/org/springframework/remoting/RemoteAccessException.html">remote access exceptions</a> is recommended.</p>
<p>Using an interface as the service representation has the additional advantage that it is pretty easy to <a href="http://www.martinfowler.com/bliki/SelfInitializingFake.html">fake or mock the service for integration testing</a>.</p>
<h3>Handling uppercase data type attributes</h3>
<p>Now it&#8217;s getting really ugly: Legacy services sometimes violate the JAVA standards of good coding and specify uppercase attribute names for their data types. This is like writing</p>
<pre class="brush: java;">
public class MyService {
     private SomeType MyFieldName;
     ...
}
</pre>
<p>And thus pretty ugly. When using spring&#8217;s <code>JaxRpcPortProxyFactoryBean</code> things are getting a lot worce: the <code>BeanDeserializer</code> will look for a getter/setter pair (property) starting with an uppercase letter &#8211; and will never find it, since they must start with a lowercase letter. <a href="http://issues.apache.org/jira/browse/AXIS-1761">This is a known issue in Axis 1.x</a>, and there is no fix released for it (and most likely there will never be one). </p>
<p>I solved the problem by providing a subclass of the <code>org.apache.axis.encoding.ser.BeanDeserializer</code> to the <code>JaxRpcPortProxyFactoryBean</code> using a subclassed <code>AxisBeanMappingServicePostProcessor</code> and a customized <code>BeanDeserializerFactory</code>:</p>
<pre class="brush: xml;">
&lt;bean id=&quot;myWebService&quot; class=&quot;org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean&quot;&gt;
...
  &lt;property name=&quot;servicePostProcessors&quot;&gt;
    &lt;list&gt;
      &lt;bean class=&quot;my.package.MyServicePostProcessor&quot;&gt;
      ....
</pre>
<p>Here is the sourcecode for this post processor, the deserializer factory and deserializer:</p>
<h4>The post processor:</h4>
<pre class="brush: java;">
package my.package;

import javax.xml.namespace.QName;
import javax.xml.rpc.encoding.TypeMapping;

import org.apache.axis.encoding.ser.BeanSerializerFactory;
import org.springframework.remoting.jaxrpc.support.AxisBeanMappingServicePostProcessor;

/**
 * This {@link AxisBeanMappingServicePostProcessor} registers a
 * {@link MyBeanDeserializerFactory} during
 * {@link #registerBeanMapping(TypeMapping, Class, QName) mapping registration}.
 *
 * @author Olaf Otto
 */
public class MyServicePostProcessor extends AxisBeanMappingServicePostProcessor {

  /**
   * Registers the {@link MyBeanDeserializer} for the given mapping.
   */
  @Override
  @SuppressWarnings(&quot;unchecked&quot;)
  protected void registerBeanMapping(TypeMapping mapping, Class javaType, QName wsdlType) {
    mapping.register(javaType, wsdlType, new BeanSerializerFactory(javaType, wsdlType), new MyBeanDeserializerFactory(javaType, wsdlType));
  }
}
</pre>
<h4>The deserializer factory:</h4>
<pre class="brush: java;">
package my.package;

import javax.xml.namespace.QName;

import org.apache.axis.encoding.Deserializer;
import org.apache.axis.encoding.ser.BeanDeserializerFactory;
import org.apache.axis.encoding.ser.EnumDeserializer;

/**
 * This {@link BeanDeserializerFactory} provides a {@link #getGeneralPurpose(String) general-purpose}
 * deserializer {@link MyBeanDeserializer capable of handling local names not conforming to the java beans spec}.
 *
 * @author Olaf Otto
 */
public class MyBeanDeserializerFactory extends BeanDeserializerFactory {
  private static final long serialVersionUID = -8880103201020594221L;

  @SuppressWarnings(&quot;unchecked&quot;)
  public MyBeanDeserializerFactory(Class javaType, QName xmlType) {
    super(javaType, xmlType);
  }

  /**
   * Returns either:
   * &amp;lt;ol&amp;gt;
   *       &amp;lt;li&amp;gt;
   *           The super types general-purpose deserializer if the target type or
   *           the XML type is &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; or the pre-defined deserializer class
   *           is an {@link EnumDeserializer}
   *      &amp;lt;/li&amp;gt;

   *      &amp;lt;li&amp;gt;A {@link MyBeanDeserializer} otherwise (standard case)&amp;lt;/li&amp;gt;
   * &amp;lt;/ol&amp;gt;
   *
   *  @return never &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, rather throws a {@link RuntimeException}.
   */
  @Override
  protected Deserializer getGeneralPurpose(String mechanismType) {
    Deserializer deserializer;
    if (javaType == null || xmlType == null || deserClass == org.apache.axis.encoding.ser.EnumDeserializer.class) {
      deserializer = super.getGeneralPurpose(mechanismType);
    } else {
      deserializer = new MyBeanDeserializer(javaType, xmlType, typeDesc, propertyMap);
    }
    return deserializer;
  }
}
</pre>
<h4>The deserializer:</h4>
<pre class="brush: java;">
package my.package;

import static org.springframework.util.Assert.hasText;

import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.axis.description.TypeDesc;
import org.apache.axis.encoding.DeserializationContext;
import org.apache.axis.encoding.ser.BeanDeserializer;
import org.apache.axis.message.SOAPHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
 * This {@link BeanDeserializer} takes care of non-normal local names,
 * i.e. local names starting with an uppercase character.&amp;lt;br /&amp;gt;
 * Such a local name, usually derived from a WSDL description, will
 * lead to a property lookup for a getter/setter pair on the target bean of the deserialization
 * with an uppercase name start. However, all property names start with a lowercase character
 * according to the java beans spec, thus a property with an upper case name start
 * is never found, causing the deserialization to fail.&amp;lt;br /&amp;gt;
 *
 * @author Olaf Otto
 */
public class MyBeanDeserializer extends BeanDeserializer {
  private static final long serialVersionUID = 1183255594948119370L;

  @SuppressWarnings(&quot;unchecked&quot;)
  public MyBeanDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc, Map propertyMap) {
    super(javaType, xmlType, typeDesc, propertyMap);
  }

  @SuppressWarnings(&quot;unchecked&quot;)
  public MyBeanDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc) {
    super(javaType, xmlType, typeDesc);
  }

  @SuppressWarnings(&quot;unchecked&quot;)
  public MyBeanDeserializer(Class javaType, QName xmlType) {
    super(javaType, xmlType);
  }

  /**
   * {@link #normalizeLocalName(String) normalizes} the localName and invokes
   * {@link BeanDeserializer#onStartChild(String, String, String, Attributes, DeserializationContext)}.
   */
  @Override
  public SOAPHandler onStartChild(String namespace, String localName, String prefix, Attributes attributes1, DeserializationContext deserializationcontext) throws SAXException {
    String normalizedLocalName = normalizeLocalName(localName);
    return super.onStartChild(namespace, normalizedLocalName, prefix, attributes1, deserializationcontext);
  }

  /**
   * Converts the first character of the local name to
   * {@link Character#toLowerCase(char) lower case}, if it is
   * {@link Character#isUpperCase(char) upper case}.
   *
   * @param localName must not be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; and not empty.
   * @return The normalized local name, never &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.
   */
  public String normalizeLocalName(String localName) {
    hasText(localName, &quot;The local name must not be null or empty.&quot;);
    char localNameStart  = localName.charAt(0);
    String normalizedLocalName = localName;
    if (Character.isUpperCase(localNameStart)) {
      StringBuilder builder = new StringBuilder(32)
                    .append(Character.toLowerCase(localNameStart))
                    .append(localName.substring(1));
      normalizedLocalName = builder.toString();
    }
    return normalizedLocalName;
  }
}
</pre>
<p>You might also write you own Serializer if you have the problem &#8220;in the opposite direction&#8221; which, luckily, I didn&#8217;t.</p>
<p>Let&#8217;s hope the current WS standards are not deprecated this fast&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://olafsblog.sysbsb.de/using-rpc-style-encoded-axis-1-webservices-with-spring-remoting/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
