Web Services with JAXRPC

Just as RMI provides a standard API for creating remote services that are exposed using binary protocols, JAXRPC provides a standard API for creating remote services that are exposed using RPC-style SOAP messages. JAXRPC is used to create SOAP-based services, called endpoints in web service lingo, and clients in an implementation-agnostic way. On the server side, JAXRPC is a fairly simple API that borrows much from RMI. The complexity lies in the definition of service endpoints, which is often an implementation-specific issue, and the code on the client side, which is convoluted and problematic.

Unfortunately, Spring cannot help much with service endpoint issues, because the way in which this functions is dependent on the JAXRPC implementation you are using. However, Spring does provide the ServletEndpointSupport class, which makes it simple for endpoints that sit behind a servlet to access a Spring ApplicationContext. On the client side, Spring provides the JaxRpcPortProxyFactoryBean class, which allows you to create a proxy to a SOAP web service, reducing the complexity inherent in creating a JAXRPC client and shielding your client application from any JAXRPC-specific details.

In this section, you create a simple web service that uses the ServletEndpointSupport class to load dependencies from a Spring ApplicationContext and you learn a useful pattern for building service endpoints when you are using Spring. In addition, you use the JaxRpcPortProxyFactoryBean to create a proxy for accessing your service and you look at Axis-specific details for handling complex Java objects in your services.

Introducing Apache Axis

For the examples in this section, we are going to use the Apache Axis SOAP stack, which provides a fully compliant JAXRPC implementation. Axis provides support for SOAP over a variety of different transports such as HTTP, JMS, and SMTP. For the examples in this section, we use the HTTP transport, which is implemented as a servlet that dispatches requests to your service automatically. HTTP is the traditional transport for SOAP and Spring provides additional support for service endpoints that sit behind a servlet.

In this section, we assume a basic knowledge of the Axis framework and JAXRPC. If you are unfamiliar with JAXRPC, refer to the tutorial mentioned at the beginning of this chapter. For a quick introduction to Axis, check out the user guide at http://ws.apache.org/axis/java/user-guide.html, which provides a rundown of Axis features and how they work.

If you downloaded the full distribution of Spring including all the dependencies, you already have Axis and its dependencies. Otherwise, you can download Axis from http://ws.apache.org/axis. For the examples in this section, we use version 1.1 of Axis.

Creating a Web Service with ServletEndpointSupport

In this section, you build a basic web service that is analogous to the "Hello World" RMI services you saw earlier. Creating JAXRPC services is where most of the implementation-specific details come into play. From a Java code perspective, everything is pretty much standard, but on a deployment front, everything is implementation-specific. If you want to build a web service using a JAXRPC implementation other than Axis, you need to modify the deployment details as appropriate for your implementation of choice.

A basic Axis web service is constructed of four parts: the Axis servlet, the Axis deployment descriptor, the remote interface, and the service implementation. The Axis servlet sits between your services and SOAP clients and is responsible for creating the Web Services Description Language (WSDL) description of your service so that a SOAP client can access your service, for translating SOAP requests into Java method calls, and for translating method return values into SOAP responses. The Axis servlet hides most of the plumbing code related to building a web service from you; indeed, you rarely, if ever, have to create code to handle SOAP messages manually because the Axis servlet takes care of all of this.

The Axis deployment descriptor is an XML document that provides information to the Axis servlet about the services you wish to expose and how they should be exposed. Axis uses the information contained in the deployment descriptor to determine which classes in your web application should be exposed via SOAP and also to obtain additional information about the service that cannot be obtained from the remote interface or the service implementation.

The remote interface is identical to the remote interface used for an RMI service. It must extend the java.rmi.Remote interface and all methods must be declared to throw RemoteException. This similarity with RMI makes it simple to reuse remote interfaces across both RMI services and web services; indeed, in this example, we reuse the RemoteHelloWorld interface from Listing 16-7 for our web service.

The service implementation is simply an implementation of the remote interface similar to the SimpleRemoteHelloWorld implementation you saw in Chapter 17, but for now, it is enough to know that Spring provides a mechanism for loading an ApplicationContext once in a web application and makes it available through the ServletContext class.

Creating the Remote Interface

When building a JAXRPC web service, the first step is to create the remote interface. As we mentioned in the previous section, JAXRPC remote interfaces are exactly the same as RMI remote interfaces. For this example, we reuse the RemoteHelloWorld interface shown in Listing 16-7.

Implementing the Web Service

After you create the remote interface for your web service, the next step is to create your service implementation class. As we mentioned previously, creating a basic service class is just like creating a service class for an RMI service—you simply implement the remote interface. Indeed, we could choose to reuse the SimpleRemoteHelloWorld class from Listing 16-8. However, this approach has a drawback—you cannot use DI. A much better solution is to make your service class a simple wrapper around a POJO service object, which is loaded from a Spring ApplicationContext and thus can be configured using DI. Spring makes this simple by providing the ServletEndpointSupport class, which allows you to access the ApplicationContext that is loaded for a web application. In Listing 16-13, you can see the JaxRpcHelloWorld class, which shows an example of this approach.

Listing 16-13: Creating a JAXRPC Service Wrapper

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import java.rmi.RemoteException;      import javax.xml.rpc.ServiceException;      import org.springframework.remoting.jaxrpc.ServletEndpointSupport;      import com.apress.prospring.ch16.remoting.HelloWorld; import com.apress.prospring.ch16.remoting.rmi.RemoteHelloWorld;      public class JaxRpcHelloWorld extends ServletEndpointSupport implements         RemoteHelloWorld {          private HelloWorld helloWorld;          protected void onInit() throws ServiceException {         helloWorld = (HelloWorld) getApplicationContext().getBean(                 "helloWorldService");     }          public String getMessage() throws RemoteException {         return helloWorld.getMessage();     } }
image from book

There are two important parts to this class: the implementation of the getMessage() method and the onInit() method. In getMessage(), you can see that the actual processing is delegated to an instance of the HelloWorld interface shown in Listing 16-1. The HelloWorld interface and the RemoteHelloWorld interface share methods with the same signature, but HelloWorld does not extend Remote and its methods do not throw RemoteException. Implementations of the HelloWorld interface can easily be tested away from the servlet container and reused in many environments because they are not coupled to the Java remote interfaces. We could have just made the JaxRpcHelloWorld class delegate to another RemoteHelloWorld, but the reusability of implementations of RemoteHelloWorld is impaired by the fact that all methods throw the checked RemoteException. By using the HelloWorld interface, we can use implementations that can be used easily in other environments.

In the onInit() method, you can see that an actual implementation of HelloWorld is loaded from a Spring ApplicationContext. The ServletEndpointSupport class provides the getApplicationContext() method, which allows you to access the ApplicationContext that is configured for the web application containing the service class. The actual mechanism for configuring this ApplicationContext is discussed later in the "Creating the Web Application Deployment Descriptor" section, but in Listing 16-14, you can see the contents of the ApplicationContext configuration file.

Listing 16-14: ApplicationContext Configuration for JaxRpcHelloWorld

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean             />
image from book

As you can see, the helloWorldService bean is defined as an instance of the SimpleHelloWorld class shown in Listing 16-2. This class implements the HelloWorld interface but has nothing to do with the RemoteHelloWorld interface implemented by the JaxRpcHelloWorld service class.

Behind the scenes, the ServletEndpointSupport class implements the javax.xml.rpc.server.ServiceLifecycle interface, which allows it to receive notifications from Axis at certain points throughout its lifecycle. Through this interface, the ServletEndpoint class is able to access the ServletContext of the currently running web application, and from there, it can access the ApplicationContext that is configured for the web application.

Creating the Axis Deployment Descriptor

Once you have created the service implementation class, you are ready to create the Axis deployment descriptor for your web service. The Axis deployment descriptor is an XML file that you should name server-config.wsdd and place in the WEB-INF directory of your web application. Listing 16-15 shows the server-config.wsdd file for the JaxRpcHelloWorld service.

Listing 16-15: Axis Deployment Descriptor for JaxRpcHelloWorld

image from book
<deployment xmlns="http://xml.apache.org/axis/wsdd/"  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">     <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>          <service name="HelloWorld" provider="java:RPC">         <parameter name="className"                 value="com.apress.prospring.ch16.remoting.jaxrpc.JaxRpcHelloWorld"/>         <parameter name="allowedMethods" value="*"/>     </service>          <transport name="http">         <requestFlow>             <handler type="URLMapper"/>         </requestFlow>     </transport> </deployment>
image from book

There are three important parts to this deployment descriptor: the <handler> tag, the <transport> tag, and the <service> tag. The <handler> tag allows you to configure a Handler that you can integrate into the control flow for a SOAP request/response sequence. The URLMapper Handler is required as part of the request flow of the HTTP transport that you configure using the <transport> tag. The URLMapper is used by Axis to map the URIs of incoming requests to service names; this is the desirable approach when you are using HTTP as the transport. For instance, if the Axis servlet is mapped to handle requests on http://localhost:8080/app/services and you have a service called FooService, then the URL for your service is http://localhost:8080/app/services/FooService. Without this Handler, your service is not accessible and Axis is not able to generate the WSDL for it because it cannot locate the service for the given URI. You can find more details about the <handler> and <transport> tags on the Axis website.

The most important tag is the <service> tag, which is used to configure the actual service. The <service> tag itself has two attributes: name and provider. The name attribute can be any value, but each service must have a valid name. Bear in mind that this name forms part of the URL for your web service, so it should be concise and you should avoid special characters that overcomplicate the URL. The provider attribute specifies what kind of web service you are creating. In this case, we specify java:RPC, indicating that this is an RPC-style service. Inside the <service> tag are two <parameter> tags—one that specifies the fully qualified name of the service implementation class and one that defines a filter for which methods of the service are exposed in the service.

Axis supports many more configuration parameters than those shown in this example, but they are outside the scope of this chapter. For more details, read the Axis reference guide at http://ws.apache.org/axis/java/reference.html.

Creating the Web Application Deployment Descriptor

Once you have created the Axis deployment descriptor, all that remains is to create the web application deployment descriptor, package the web application for deployment, and then deploy it in your servlet container of choice.

In the deployment descriptor for a web service built using ServletEndpointSupport, you need to configure not only the Axis servlet, but also the Spring ContextLoaderServlet (or ContextLoaderListener for 2.4 servlet containers) to load the ApplicationContext for your web service. Listing 16-16 shows an example of this configuration for the JaxRpcHelloWorld service.

Listing 16-16: Configuring the Axis Servlet and Spring ContextLoaderServlet

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"                          "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app>     <context-param>         <param-name>contextConfigLocation</param-name>         <param-value>/WEB-INF/applicationContext.xml</param-value>     </context-param>          <servlet>         <servlet-name>context</servlet-name>         <servlet-class>             org.springframework.web.context.ContextLoaderServlet         </servlet-class>         <load-on-startup>1</load-on-startup>     </servlet>          <servlet>         <servlet-name>axis</servlet-name>         <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>         <load-on-startup>2</load-on-startup>     </servlet>          <servlet-mapping>         <servlet-name>axis</servlet-name>         <url-pattern>/services/*</url-pattern>     </servlet-mapping> </web-app> 
image from book

Here you can see a deployment descriptor that configures two servlets: the Spring ContextLoaderServlet and the Axis AxisServlet class. The ContextLoaderServlet loads an ApplicationContext using the path provided in the contextConfigLocation context parameter and then stores it in the ServletContext. Without this servlet declaration, the ServletEndpointSupport class is not able to locate the ApplicationContext, and thus JaxRpcHelloWorld cannot load the helloWorldService bean it requires. You should always ensure that the ContextLoaderServlet is loaded before all other servlets by explicitly setting the <load-on-startup> parameter for each servlet. This avoids problems arising when a servlet attempts to access an ApplicationContext that is not yet loaded.

The configuration of the Axis service is fairly basic and you should be more than familiar with the code you see. Note that the choice of URL mapping was not arbitrary—Axis generates a web page for your services that expects them to be mapped under /services/*. Figure 16-1 shows an example of the page generated by Axis for the HelloWorld service configured in Listing 16-16.

image from book
Figure 16-1: Viewing web services in your web browser

Clicking the wsdl link next to the listing for the HelloWorld service brings up the WSDL definition for your service. It is evident from looking at the WSDL that Axis is saving you an awful lot of work by generating the WSDL automatically. We certainly would not want to have to generate a WSDL document manually for every service we create.

Accessing RPC-Style Web Services Using Proxies

Traditionally, accessing a web service using JAXRPC has been quite a complex process that bears little resemblance to working with the service interface of the web service. Although some web service toolkits, Axis included, provide the ability to generate proxies to a web service, these are generally implementation-dependent and might not function exactly as you would expect.

Thankfully, Spring provides a JAXRPC proxy generator that allows you to access any RPC- style SOAP service using a simple Java interface. In this example, you learn how to configure an instance of JaxRpcPortProxyFactoryBean to create a proxy to the HelloWorld service that you built and deployed in the previous section.

One of the biggest benefits of using a Spring proxy for accessing JAXRPC services is that you can have the proxy implement a business interface rather than the remote interface of the JAXRPC service. The remote interface is still required so that Spring can use it internally, but externally, you can interact with a proxy through the business interface. In this example, you see how to construct a proxy to the HelloWorld service that uses the HelloWorld interface shown in Listing 16-1 rather than the RemoteHelloWorld interface that is implemented by the JaxRpcHelloWorld service class.

Configuring the JaxRpcPortProxyFactoryBean

Creating a JAXRPC proxy is very similar to creating an RMI proxy—all you need to do is configure an instance of the JaxRpcPortProxyFactoryBean in your ApplicationContext. This instance can then be accessed in your application using your chosen business interface. Listing 16-17 shows an example configuration that uses JaxRpcPortProxyFactoryBean to create a proxy to the HelloWorld service.

Listing 16-17: Configuring a JAXRPC Proxy

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean               >         <property name="serviceFactoryClass">             <value>org.apache.axis.client.ServiceFactory</value>         </property>         <property name="wsdlDocumentUrl">             <value>http://localhost:8080/remoting/services/HelloWorld?wsdl</value>         </property>         <property name="namespaceUri">             <value>http://localhost:8080/remoting/services/HelloWorld</value>         </property>         <property name="serviceName">             <value>JaxRpcHelloWorldService</value>         </property>         <property name="portName">             <value>HelloWorld</value>         </property>         <property name="portInterface">             <value>com.apress.prospring.ch16.remoting.rmi.RemoteHelloWorld</value>         </property>         <property name="serviceInterface">             <value>com.apress.prospring.ch16.remoting.HelloWorld</value>         </property>     </bean> </beans>
image from book

Here you can see that we configured an instance of JaxRpcPortProxyFactoryBean with values for seven properties. The values for four of these properties—serviceFactoryClass, wsdlDocumentUrl, portInterface, and serviceInterface—are simple to obtain. Obtaining values for the rest requires peeking inside the WSDL that is generated for your service.

The serviceFactoryClass is used to point Spring at the implementation of the javax.xml.rpc.ServiceFactory interface provided by your SOAP stack. In the case of Axis, this class is org.apache.axis.client.ServiceFactory.

The wsdlDocumentUrl, obviously, specifies the URL of the WSDL document for your service; you can obtain it by browsing to your service in a web browser and clicking the wsdl link.

The serviceInterface property allows you to specify which interface you want the generated proxy to implement. If this is the remote interface of the JAXRPC service, you do not need to provide a value for the portInterface property; otherwise, you must set portInterface to the fully qualified name of the remote interface. As you can see, we specify that the proxy should implement the HelloWorld interface, which is not a remote interface, so we also specify that the portInterface is RemoteHelloWorld. In general, it is better to use a separate service interface for your proxies rather than the remote interface because it removes the need for your application code to deal with RemoteExceptions explicitly.

To obtain values for the namespaceUri, portName, and serviceName properties, you need to look at the WSDL generated for your service. The root node of the WSDL document is <wsdl:definitions>, which has an attribute, targetNamespace, the value of which corresponds to the value of the namespaceUri property.

Toward the end of your WSDL document, you will see a snippet of code similar to that shown in Listing 16-18.

Listing 16-18: Service Definition in WSDL

image from book
<wsdl:service name="JaxRpcHelloWorldService">     <wsdl:port binding="impl:HelloWorldSoapBinding" name="HelloWorld">       <wsdlsoap:address                  location="http://localhost:8080/remoting/services/HelloWorld"/>     </wsdl:port>   </wsdl:service>
image from book

Here the value of the name attribute of the <wsdl:service> tag corresponds to the value of the serviceName property, and the value of the name attribute of the <wsdl:port> tag corresponds to the value of the portName property.

These seven properties provide the JaxRpcPortProxyFactoryBean class with all the information it needs to generate a proxy that implements the HelloWorld interface but internally uses the RemoteHelloWorld interface. The proxy is generated using the WSDL document at http://localhost:8080/remoting/services/HelloWorld?wsdl and it expects the target namespace of this WSDL document to be http://localhost:8080/remoting/services/HelloWorld.

Using JAXRPC Proxies

As with RmiProxyFactoryBean, the JaxRpcPortProxyFactoryBean class implements the Factory- Bean interface so that when it is accessed using ApplicationContext.getBean() it returns the proxy instead of a reference to itself. Listing 16-19 shows the HelloWorldClient class, which loads an ApplicationContext using the configuration shown in Listing 16-17, grabs the proxy, and uses it to execute the getMessage() method of the web service.

Listing 16-19: Using a JAXRPC Proxy

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.ch16.remoting.HelloWorld;      public class HelloWorldClient {          public static void main(String[] args) {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch16/src/conf/jaxrpc/client.xml");              HelloWorld helloWorld = (HelloWorld)ctx.getBean("helloWorldService");         System.out.println(helloWorld.getMessage());     } }
image from book

As you can see from this example, the client is free to interact with the web service using the HelloWorld interface, which is much simpler than the RemoteHelloWorld interface because it does not require code to deal with the checked RemoteExceptions.

Make sure that your web service is deployed in your servlet container and that the servlet container is running, then run this application. After a short delay, you should see the message "Hello World" written to the console. As this example shows, building a web service client is extremely simple when you are using Spring, thanks to the support for proxies. By allowing proxies to implement a business interface that is separate from the remote interface of a service, Spring also makes the code required to interact with the proxy much simpler as well.

Working with JavaBeans in Axis Services

In the previous example, the value returned by the getMessage() method of the HelloWorld service was a simple String, which is mapped directly to the xsd:string type in SOAP. This mapping is handled automatically by Axis, but when you use complex return types such as JavaBeans, Axis cannot map the type automatically, and using such a type requires additional configuration at both the server and client side.

As an example of this, consider the remote interface shown in Listing 16-20.

Listing 16-20: The MessageService Interface

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import java.rmi.Remote; import java.rmi.RemoteException;      import com.apress.prospring.ch16.remoting.MessageBean;      public interface MessageService extends Remote {          MessageBean getMessage() throws RemoteException; }
image from book

Here you can see that the getMessage() method of the MessageService interface does not return a String like RemoteHelloWorld.getMessage(); instead, it returns an instance of the MessageBean, the code for which is shown in Listing 16-21.

Listing 16-21: The MessageBean Class

image from book
package com.apress.prospring.ch16.remoting;      import java.io.Serializable;      public class MessageBean implements Serializable {          private String message;     private String senderName;          public String getMessage() {         return message;     }          public void setMessage(String message) {         this.message = message;     }          public String getSenderName() {         return senderName;     }          public void setSenderName(String senderName) {         this.senderName = senderName;     }          public String toString() {             return "Message: " + message + "\nSender: " + senderName;     } }
image from book

As you can see, MessageBean is just a simple JavaBean class with two properties, both of type String. You will need additional configuration when you use this class as part of an Axis service, as the return type of a method or as a method parameter.

Note 

Although the MessageBean class implements Serializable, this is not a requirement of JAXRPC. MessageBean is used in a later example that does require it to implement Serializable.

Deploying a Service with Complex Types

On the server side, when configuring your service in server-config.wsdd, you need to specify additional configuration details that tell Axis how it should serialize and deserialize your complex types to and from SOAP messages. Axis provides built-in support for serializing and deserializing JavaBean-style classes; this means that other than a small amount of additional configuration information, you do not need to expend any effort to use JavaBeans in your web services.

For non-JavaBean-style classes, you must provide custom implementations of the SerializerFactory and DeserializerFactory interfaces. Indeed, behind the scenes, Axis' Java- Beans support is implemented using prebuilt implementations of these interfaces. The topic of custom serialization is outside the scope of this chapter, but you can find more details online in the Axis User Guide.

To configure a JavaBean for use with an Axis web service, you need to add a <beanMapping> definition to the <service> definition for your service. In Listing 16-22, you can see the <service> definition for the MessageService complete with <beanMapping>.

Listing 16-22: Configuring a <beanMapping>

image from book
<service name="MessageService" provider="java:RPC">         <parameter name="className"             value="com.apress.prospring.ch16.remoting.jaxrpc.JaxRpcMessageService"/>         <parameter name="allowedMethods" value="*"/>         <beanMapping qname="apress:MessageBean"                  xmlns:apress="http://www.apress.com"                      languageSpecificType="¿                     java:com.apress.prospring.ch16.remoting.jaxrpc.MessageBean"/>     </service>
image from book

Here you can see that we have defined a <beanMapping> tag for the MessageBean class, specifying that the XML-qualified name of the MessageBean class in SOAP form should be apress:MessageBean, where apress is the namespace and MessageBean is the local name. In addition, we map the apress namespace to the URI http://www.apress.com.

From the service configuration in Listing 16-22, note also that the implementation class is set to JaxRpcMessageService; the code for this is shown in Listing 16-23.

Listing 16-23: The JaxRpcMessageService Class

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import java.rmi.RemoteException;      import com.apress.prospring.ch16.remoting.MessageBean;      public class JaxRpcMessageService implements MessageService {          public MessageBean getMessage() throws RemoteException {         MessageBean bean = new MessageBean();         bean.setMessage("Hello World!");         bean.setSenderName("Rob Harrop");         return bean;     }      }
image from book

This implementation is trivial and requires no explanation. At this point, you are ready to deploy your web service. Once the web service is deployed in your servlet container, you should navigate to the WSDL for it to ensure that the <beanMapping> has been correctly applied. In Listing 16-24, you can see a snippet of the WSDL from the MessageService that is created by the <beanMapping>.

Listing 16-24: Complex Types in WSDL

image from book
    <wsdl:types>         <schema targetNamespace="http://www.apress.com"                                       xmlns="http://www.w3.org/2001/XMLSchema">             <import namespace="http://schemas.xmlsoap.org/soap/encoding/" />             <complexType name="MessageBean">                 <sequence>                     <element name="message" nillable="true" type="xsd:string" />                     <element name="senderName" nillable="true" type="xsd:string" />                 </sequence>             </complexType>         </schema>     </wsdl:types>
image from book

In the definition of the MessageBean <complexType>, the name for which is derived from the qname attribute of your <beanMapping>, you can see that Axis has successfully defined two attributes, message and senderName, based on the JavaBean properties of the MessageBean class.

Accessing a Service with Complex Types

Once you have successfully deployed a service using complex types, all that remains is to create a client application to access the service. Unfortunately, in Spring, there is no declarative way to define complex type mappings for use with JAXRPC clients. Instead, you must subclass the JaxRpcPortProxyFactoryBean class to post-process the JAXRPC service created by the proxy creator and add your custom type mappings.

When registering custom type mappings, you are essentially informing your JAXRPC framework which implementations of the SerializerFactory and DeserializerFactory classes to use for a given type. Although the code to register a custom type mapping is largely provider- independent, the implementations of SerializerFactory and DeserializerFactory are either supplied by your SOAP stack or by your application. In this case, we use the BeanSerializerFactory and BeanDeserializerFactory classes provided by Axis when we register our custom type mapping. This is shown in Listing 16-25.

Listing 16-25: Registering a Custom Type Mapping

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import javax.xml.namespace.QName; import javax.xml.rpc.Service; import javax.xml.rpc.encoding.TypeMapping; import javax.xml.rpc.encoding.TypeMappingRegistry;      import org.apache.axis.encoding.ser.BeanDeserializerFactory; import org.apache.axis.encoding.ser.BeanSerializerFactory; import org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean;      import com.apress.prospring.ch16.remoting.MessageBean;      public class MessageServiceJaxRpcProxyFactoryBean extends         JaxRpcPortProxyFactoryBean {          protected void postProcessJaxRpcService(Service service) {        TypeMappingRegistry tmr = service.getTypeMappingRegistry();        TypeMapping tm = tmr.createTypeMapping();                QName qname = new QName("http://www.apress.com", "MessageBean");                tm.register(MessageBean.class, qname,                 new BeanSerializerFactory(MessageBean.class, qname),                 new BeanDeserializerFactory(MessageBean.class, qname));                tmr.register("http://schemas.xmlsoap.org/soap/encoding/", tm);     } }
image from book

To register a custom type mapping in your custom JaxRpcPortProxyFactoryBean implementation, you must override the postProcessJaxRpcService() method, which allows you to access the Service instance once it is configured by the base class. The code in Listing 16-25 is fairly self-explanatory—if you cannot follow the code, you should refer to the JAXRPC JavaDocs, which are available from http://java.sun.com/xml/jaxrpc/.

To access a service that uses complex types using a proxy, replace JaxRpcPortProxyFactoryBean with your custom implementation in the ApplicationContext configuration. In this case, we replace JaxRpcPortProxyFactoryBean with MessageServiceJaxRpcProxyFactoryBean, as shown in Listing 16-26.

Listing 16-26: Proxy Configuration Using Custom Proxy Factory

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean                  symbol">¿    com.apress.prospring.ch16.remoting.jaxrpc.MessageServiceJaxRpcProxyFactoryBean">         <property name="serviceFactoryClass">             <value>org.apache.axis.client.ServiceFactory</value>         </property>         <property name="wsdlDocumentUrl">             <value>                  http://localhost:8080/remoting/services/MessageService?wsdl             </value>         </property>         <property name="namespaceUri">             <value>http://localhost:8080/remoting/services/MessageService</value>         </property>         <property name="serviceName">             <value>JaxRpcMessageServiceService</value>         </property>         <property name="portName">             <value>MessageService</value>         </property>         <property name="serviceInterface">             <value>com.apress.prospring.ch16.remoting.jaxrpc.MessageService</value>         </property>     </bean> </beans>
image from book

As you can see, the proxy is configured in the same way as it would be if you used the standard JaxRpcPortProxyFactoryBean class; the only thing that changes is the class used in the <bean> declaration. Notice that in this example, we use the remote interface as the value for the serviceInterface property; this means that the proxy itself actually implements the remote interface of the service. We do this only for illustration purposes—it is certainly preferable to use a nonremote business interface as the interface for your proxy.

Using the proxy from your application code is no different than working with any other object that implements the MessageService interface, as shown by the code in Listing 16-27.

Listing 16-27: The MessageServiceClient Class

image from book
package com.apress.prospring.ch16.remoting.jaxrpc;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.ch16.remoting.MessageBean;      public class MessageServiceClient {          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(         "./ch16/src/conf/jaxrpc/messageServiceClient.xml");         MessageService service = (MessageService)ctx.getBean("messageService");         MessageBean bean = service.getMessage();         System.out.println(bean);     } }
image from book

Provided that the MessageService is deployed in your servlet container, running this example results in the following output to the console after a short delay:

Message: Hello World! Sender: Rob Harrop



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net