Web Ser vices via JAX-RPC


To be able to access and implement Web Services based on WSDL (Web Service Definition Language) and SOAP (Simple Object Access Protocol), Spring integrates with the standard JAX-RPC API.

The JAX-RPC specification defines client access and two service endpoint models: one for servlet containers, and one for EJB containers. JAX-RPC support is required for J2EE 1.4–compliant servers, which have to support both endpoint models. There are also a variety of standalone providers that support JAX-RPC client access and the servlet endpoint model: for example, Apache Axis (http://ws.apache.org/axis).

For client access, JAX-RPC uses RMI-style stubs: Service interfaces have to follow traditional RMI conventions, that is, extend java.rmi.Remote and throw java.rmi.RemoteException on each method. As an alternative, there is also the Dynamic Invocation Interface, which allows invocation of methods by name (similar to Java reflection).

Spring supports both the client and the server side of JAX-RPC: Spring-style client access with service interfaces, and servlet endpoints that can easily delegate to Spring-managed service implementations. The two main components involved are:

  • org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean: Proxy factory for Spring-compliant proxies that communicate with backend Web Services. To be defined in a Spring bean factory or application context, exposing the proxy object for references (through the FactoryBean mechanism). Proxies will either throw RMI's RemoteException or Spring's generic RemoteAccessException in case of remote invocation failure, depending on the type of service interface used.

  • org.springframework.remoting.jaxrpc.ServletEndpointSupport: Convenient base class for Web Service endpoints, that is, for service classes to be exposed as Web Services via a JAX-RPC–compliant tool. The Web Service tool itself will instantiate and manage such an endpoint class, which needs to implement the corresponding RMI service interface. With standard JAX-RPC, it is not possible to export an existing Spring-managed bean directly as a Web Service; an endpoint adapter is always needed.

Furthermore, there is also a LocalJaxRpcServiceFactoryBean, which sets up a JAX-RPC Service (javax.xml.rpc.Service) instance. Such a Service reference can be used, for example, to perform dynamic JAX-RPC invocations instead of method calls on a proxy interface. This is rarely used in typical application code, however.

While client access is as similar to the other remoting protocols supported in Spring as possible (the main difference being that significantly more configuration settings are required), service export is completely different: Rather than defining seamless exporters for existing Spring-managed beans, a special service endpoint class has to be written for each service, registered with the chosen Web Service tool in a proprietary fashion. The service endpoint class can in turn delegate to Spring-managed beans, but its lifecycle is not under control of Spring — it is managed by the Web Service tool.

Note that by default, only primitive types and collections can be serialized via JAX-RPC because Web Services and thus JAX-RPC do not provide a general serialization mechanism. Application-specific classes such as Order and Product in Spring's JPetStore sample application need to be addressed in a specific fashion, for example registering custom serializers and deserializers for them. Axis includes such a serializer for JavaBeans, which is useful for typical domain objects.

Important 

WSDL/SOAP-based Web Services are an obvious choice for cross-platform remoting, for example between J2EE and .NET. However, setup and configuration are significantly more complex than with Hessian or HTTP invoker, and the parsing overhead of SOAP is significantly higher.

Hessian and HTTP invoker are usually preferable alternatives for HTTP-based Java-to- Java remoting. In particular, their serialization power makes them attractive for the transfer of complex Java objects. We recommend that you resort to SOAP only if there is a concrete need for it, that is, if there are non-Java clients with existing SOAP support.

It is important to note that remoting protocol is not an either/or choice when using Spring. The same target service can be exported via both Hessian and SOAP at the same time, for example, through defining a corresponding exporter for Hessian and referring to the same service from a JAX-RPC servlet endpoint — available at different URLs. Clients can then choose the protocol for talking to the service: Java clients might prefer Hessian, while .NET clients will choose SOAP.

Accessing a Service

On the client side, a proxy for a target service can easily be created via a Spring bean definition — analogous to RMI. The configuration needed is as follows, assuming Axis as a concrete backend implementation. For details on the parameters, see the JAX-RPC specification or the Axis documentation.

<bean       >   <property name="serviceFactoryClass">     <value>org.apache.axis.client.ServiceFactory</value>   </property>   <property name="wsdlDocumentUrl">     <value>http://localhost:8080/axis/OrderService?wsdl</value>   </property>   <property name="namespaceUri">     <value>http://localhost:8080/axis/OrderService</value>   </property>   <property name="serviceName">     <value>JaxRpcOrderServiceService</value>   </property>   <property name="portName">     <value>OrderService</value>   </property>   <property name="serviceInterface">     <value>org.springframework.samples.jpetstore.service.RemoteOrderService</value>   </property> </bean>

The service interface used here is an RMI interface version of the OrderService used in the earlier examples. It might have been generated by a Web Service tool that turns a WSDL service description into a Java interface (usually following RMI conventions).

public interface RemoteOrderService extends java.rmi.Remote {       Order getOrder(int orderId) throws java.rmi.RemoteException; }

Alternatively, the service interface can also be the same plain Java business interface used in the earlier examples, not deriving from specific base interfaces or throwing special remoting exceptions. On remote invocation failure, it will throw Spring's unchecked RemoteAccessException.

public interface OrderService {       Order getOrder(int orderId); } 

The corresponding proxy bean definition looks as follows. It defines the plain Java interface as serviceInterface and the (potentially generated) RMI interface as JAX-RPC portInterface. Each method call on the service interface will be delegated to the corresponding method on the RMI interface, converting the checked RMI RemoteException into Spring's unchecked RemoteAccessException.

<bean       >   <property name="serviceFactoryClass">     <value>org.apache.axis.client.ServiceFactory</value>   </property>   <property name="wsdlDocumentUrl">     <value>http://localhost:8080/axis/OrderService?wsdl</value>   </property>   <property name="namespaceUri">     <value>http://localhost:8080/axis/OrderService</value>   </property>   <property name="serviceName">     <value>JaxRpcOrderServiceService</value>   </property>   <property name="portName">     <value>OrderService</value>   </property>   <property name="serviceInterface">     <value>org.springframework.samples.jpetstore.domain.logic.OrderService</value>   </property>   <property name="portInterface">     <value>org.springframework.samples.jpetstore.service.RemoteOrderService</value>   </property> </bean>

Alternatively, you can also omit the portInterface value, which will turn service invocations into dynamic JAX-RPC calls (using JAX-RPC's Dynamic Invocation Interface). Dynamic calls work nicely on Axis, for example, which means you do not need to maintain an RMI port interface. While specifying an RMI port interface might offer advantages on certain JAX-RPC implementations, it is often preferable to stick with a single plain Java business interface instead, using dynamic calls underneath.

Important 

Similar to its RMI support equivalent, JaxRpcPortProxyFactoryBean is able to access a JAX-RPC port through a plain Java business interface, automatically converting checked RemoteExceptions to unchecked RemoteAccessExceptions. This enables you to access standard JAX-RPC services in a consistent Spring proxy fashion.

Such a proxy is then available for bean references; for example:

<bean  >   <property name="orderService">     <ref bean="jaxRpcProxy"/>   </property> </bean>

The MyOrderServiceClient class is the same as in the earlier example. It would simply expose a bean property of type org.springframework.samples.jpetstore.domain.logic.OrderService here, so the client is tied not to a remote service but rather to a plain Java business interface.

public class MyOrderServiceClient {       private OrderService orderService;       public void setOrderService(OrderService orderService) {     this.orderService = orderService;   }       public void doSomething() {     Order order = this.orderService.getOrder(...);     ...    } }

Exporting a Service

The biggest difference in setting up a JAX-RPC service is on the server side. In contrast to all other supported remoting strategies in Spring, this is not possible through an exporter definition in a Spring context. Instead, the actual deployment is tool-specific; just the service endpoint model is standardized by the JAX-RPC specification.

A JAX-RPC servlet endpoint is a plain Java class that implements the RMI-style port interface of the service. It can optionally implement the javax.xml.rpc.server.ServiceLifecycle interface, to receive a javax.xml.rpc.server.ServletEndpointContext on initialization, which in turn allows access to the javax.servlet.ServletContext of the web application. This can be used to access the Spring root application context, in turn delegating to Spring-managed middle tier beans for service operations.

Spring provides the org.springframework.remoting.jaxrpc.ServletEndpointSupport class as a convenient base class for such endpoint implementations: It pre-implements the ServiceLifecycle interface and the Spring root application context lookup, allowing easy access to context facilities.

In the case of JPetStore's OrderService, the endpoint implementation looks as follows. It derives from ServletEndpointSupport and implements the plain Java business interface of the service, simply delegating a getOrder call to the target OrderService in the root application context.

 public class JaxRpcOrderService extends ServletEndpointSupport     implements OrderService {       private OrderService orderService;       protected void onInit() {     this.orderService =         (OrderService) getWebApplicationContext().getBean("petStore");   }       public Order getOrder(int orderId) {     return this.orderService.getOrder(orderId);   } } 

As shown in the earlier examples, the actual OrderService implementation in JPetStore's middle tier is the PetStoreImpl object, which also implements the PetStoreFacade interface. Therefore, the endpoint class looks up the petStore bean from the application context.

An endpoint class such as the one in the preceding example, just implementing a business interface, will be accepted by most JAX-RPC implementations, including Axis. However, the JAX-RPC specification requires an endpoint to implement an RMI port interface, so strict JAX-RPC implementations might reject such a plain endpoint class. In that case, simply let your endpoint class implement an RMI port interface, too, mirroring the business interface but complying to RMI conventions.

Important 

It is good practice to let the endpoint class implement both the RMI service interface and the plain Java business interface, if both are used. Those interfaces will just differ in the remote exception thrown; as the target service is not supposed to throw remote invocation failure exceptions itself, its methods can easily comply to both interfaces(through not throwing any such exception).

An endpoint class needs to be implemented for each exported service, even if just delegating to the corresponding target method of the corresponding Spring-managed bean. It acts as an adapter between the JAX-RPC endpoint model and the plain Java nature of the target service implementation.

As an example of tool-specific deployment of an endpoint class, let's superficially look at how to deploy to Axis. Essentially, you need to add a service section to the Axis server-config.wsdd file, which resides in the WEB-INF directory:

<service name="OrderService" provider="java:RPC">   <parameter name="allowedMethods" value="*"/>   <parameter name="className" value=       "org.springframework.samples.jpetstore.service.server.JaxRpcOrderService"/> </service>

The Axis servlet will read this configuration on startup and make the service available under the given port name, instantiating the specified endpoint class and delegating remote calls to it. The Axis servlet itself needs to be defined in web.xml:

<servlet>   <servlet-name>axis</servlet-name>   <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>   <load-on-startup/> </servlet>     <servlet-mapping>   <servlet-name>axis</servlet-name>   <url-pattern>/axis/*</url-pattern> </servlet-mapping>

We have deliberately omitted custom type mappings here, for simplicity's sake. In the next section, we will look at how to register custom serializers with Axis, on both the server and the client side.

Important 

Even though deployment of a Web Service via Axis is completely different from all other supported remoting strategies in Spring, it can still coexist with other remote exporters. The endpoints deployed via Axis will usually delegate to the same Spring-managed middle tier beans as other remote exporters.

For example, you can define a Spring DispatcherServlet for exporting services via Hessian, Burlap, and/or HTTP invoker, while also defining an AxisServlet for exporting via SOAP. This is the case in the JPetStore sample application, using the servlet names remoting and axis, respectively.

Custom Type Mappings

To allow Axis to serialize custom application objects, special type mappings have to be registered. On the server side, this usually happens through beanMapping tags nested within the service definition in the server-config.wsdd file:

<service name="OrderService" provider="java:RPC">   <parameter name="allowedMethods" value="*"/>   <parameter name="className" value=       "org.springframework.samples.jpetstore.service.server.JaxRpcOrderService"/>   <beanMapping qname="jpetstore:Order" xmlns:jpetstore="urn:JPetStore"       languageSpecificType=       "java:org.springframework.samples.jpetstore.domain.Order"/>   <beanMapping qname="jpetstore:LineItem" xmlns:jpetstore="urn:JPetStore"       languageSpecificType=       "java:org.springframework.samples.jpetstore.domain.LineItem"/>   <beanMapping qname="jpetstore:Item" xmlns:jpetstore="urn:JPetStore"       languageSpecificType=       "java:org.springframework.samples.jpetstore.domain.Item"/>   <beanMapping qname="jpetstore:Product" xmlns:jpetstore="urn:JPetStore"       languageSpecificType=       "java:org.springframework.samples.jpetstore.domain.Product"/> </service>

Each mapping defines a specific symbolic name in the WSDL namespace and associates a Java class name with it. When encountering such objects, Axis will automatically serialize them as JavaBeans, as indicated by the beanMapping tag.

On the client side, the registration of custom serializers usually happens programmatically. Spring's JaxRpcPortProxyFactoryBean offers a specific hook to register such serializers, namely the JaxRpcServicePostProcessor interface:

 public class BeanMappingServicePostProcessor     implements JaxRpcServicePostProcessor {       public void postProcessJaxRpcService(Service service) {     TypeMappingRegistry registry = service.getTypeMappingRegistry();     TypeMapping mapping = registry.createTypeMapping();     registerBeanMapping(mapping, Order.class, "Order");     registerBeanMapping(mapping, LineItem.class, "LineItem");     registerBeanMapping(mapping, Item.class, "Item");     registerBeanMapping(mapping, Product.class, "Product");     registry.register("http://schemas.xmlsoap.org/soap/encoding/", mapping);   }       protected void registerBeanMapping(TypeMapping mapping, Class type, String name){     QName qName = new QName("urn:JPetStore", name);     mapping.register(type, qName,         new BeanSerializerFactory(type, qName),         new BeanDeserializerFactory(type, qName));   } }

The corresponding bean definition looks as follows. An instance of the BeanMappingServicePostProcessor class is registered through JaxRpcPortProxyFactoryBean's servicePostProcessors property, in this case as an unnamed inner bean:

<bean      >   <property name="serviceFactoryClass">     <value>org.apache.axis.client.ServiceFactory</value>   </property>   <property name="wsdlDocumentUrl">     <value>http://localhost:8080/axis/OrderService?wsdl</value>   </property>   <property name="namespaceUri">     <value>http://localhost:8080/axis/OrderService</value>   </property>   <property name="serviceName">     <value>JaxRpcOrderServiceService</value>   </property>   <property name="portName">     <value>OrderService</value>   </property>   <property name="serviceInterface">     <value>org.springframework.samples.jpetstore.domain.logic.OrderService</value>   </property>   <property name="servicePostProcessors">     <list>       <bean background-color:#c0c0c0">           BeanMappingServicePostProcessor"/>     </list>   </property> </bean>

With both ends properly configured, Order, LineItem, Item, and Product objects can be transferred through the Web Service. For details on custom type mappings, please refer to the Axis documentation.

Important 

Custom types require special effort with WSDL/SOAP-based Web Services. In contrast to other remoting protocols such as Hessian or HTTP invoker, each custom type needs to be registered with an appropriate serializer/deserializer on both the server and the client side.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

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