Java web services APIs - Java-Springs

Spring provides full support for standard Java web services APIs:

  • Exposing web services using JAX-RPC
  • Accessing web services using JAX-RPC
  • Exposing web services using JAX-WS
  • Accessing web services using JAX-WS

In addition to stock support for JAX-RPC and JAX-WS in Spring Core, the Spring portfolio also features Spring Web Services, a solution for contract-first, document-driven web services - highly recommended for building modern, future-proof web services.

Exposing servlet-based web services using JAX-RPC
Spring provides a convenience base class for JAX-RPC servlet endpoint implementations - ServletEndpointSupport. To expose our AccountService we extend Spring's ServletEnd pointSupport class and implement our business logic here, usually delegating the call to the business layer.

/**
* JAX-RPC compliant RemoteAccountService implementation
that simply delegates

* to the AccountService implementation in the
root web application context.

*
* This wrapper class is necessary
because JAX-RPC requires working with dedicated

* endpoint classes.If an existing service
needs to be exported, a wrapper that

* extends ServletEndpointSupport for simple
application context access is

* the simplest JAX-RPC compliant way.
*
* This is the class registered with the
server-side JAX-RPC implementation.

* In the case of Axis, this happens in
"server-config.wsdd" respectively via

* deployment calls. The web service engine manages
the lifecycle of instances

* of this class: A Spring application context
can just be accessed here.

*/import org.springframework.remoting.jaxrpc. ServletEndpointSupport;
public class AccountServiceEndpoint extends ServletEndpointSupport implements RemoteAccountService {
private AccountService biz;
protected void onInit() {
this.biz = (AccountService) getWebApplicationContext() .getBean("accountService");
}
public void insertAccount(Account acc) throws RemoteException {
biz.insertAccount(acc);
}
public Account[] getAccounts(String name)throws RemoteException {
return biz.getAccounts(name);
}
}

Our AccountServletEndpoint needs to run in the same web application as the Spring context to allow for access to Spring's facilities. In case of Axis, copy the AxisServlet definition into your 'web.xml', and set up the endpoint in 'server-config.wsdd' (or use the deploy tool). See the sample application JPetStore where the OrderService is exposed as a web service using Axis.

Accessing web services using JAX-RPC
Spring provides two factory beans to create JAX-RPC web service proxies, namely LocalJaxRpcServiceFactoryBean and JaxRpcPortProxyFactoryBean. The former can only return a JAX-RPC service class for us to work with. The latter is the full-fledged version that can return a proxy that implements our business service interface. You will see that Spring has great support for web services requiring little coding efforts - most of the setup is done in the Spring configuration file as usual:

<bean id="accountWebService" class="org.springframework .remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface" value="example. RemoteAccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/>
<property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountPort"/>
</bean>

Where serviceInterface is our remote business interface the clients will use. wsdlDocumentUrl is the URL for the WSDL file. Spring needs this at startup time to create the JAX-RPC Service. namespaceUri corresponds to the targetNamespace in the .wsdl file.

serviceName corresponds to the service name in the .wsdl file. portName corresponds to the port name in the .wsdl file.
Accessing the web service is now very easy as we have a bean factory for it that will expose it as RemoteAccountService interface. We can wire this up in Spring:

<bean id="client" class="example.AccountClientImpl">
...
<property name="service" ref="accountWebService"/>
</bean>

From the client code we can access the web service just as if it was a normal class, except that it throws RemoteException.

public class AccountClientImpl {
private RemoteAccountService service;
public void setService(RemoteAccountService service) {
this.service = service;
}
public void foo() {
try {
service.insertAccount(...);
}
catch (RemoteException ex) {
// ouch
}
}
}

We can get rid of the checked RemoteException since Spring supports automatic conversion to its corresponding unchecked RemoteException. This requires that we provide a non-RMI interface also. Our configuration is now:

<bean id="accountWebService" class="org.springframework .remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface" value="example. AccountService"/>
<property name="portInterface" value="example. RemoteAccountService"/>
...
</bean>

Where serviceInterface is changed to our non RMI interface. Our RMI interface is now defined using the property portInterface. Our client code can now avoid handling java.rmi. Remote Exception:

public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}

Note that you can also drop the "port Inter face " part and specify a plain business interface as " service Inter face". In this case, Jax Rpc Port Proxy Factory Bean will automatically switch to the JAX-RPC "Dynamic Invocation Interface", performing dynamic invocations without a fixed port stub.
The advantage is that you don't even need to have an RMI-compliant Java port interface around (e.g. in case of a non-Java target web service); all you need is a matching business interface. Check out Jax Rpc Port Proxy Factory Bean's javadoc for details on the runtime implications.

Registering JAX-RPC Bean Mappings
To transfer complex objects over the wire such as Account we must register bean mappings on the client side.

We will use Axis to register bean mappings on the client side. To do this we need to register the bean mappings programmatically:

public class AxisPortProxyFactoryBean extends
JaxRpcPortProxyFactoryBean {
protected void postProcessJaxRpcService
(Service service) {
TypeMappingRegistry registry = service. getTypeMappingRegistry();
TypeMapping mapping = registry.createTypeMapping();
registerBeanMapping(mapping, Account.class, "Account");
registry.register("http://schemas.xmlsoap.org/ soap/encoding/", mapping);
}
protected void registerBeanMapping(TypeMapping mapping, Class type, String name) {
QName qName = new QName("http://localhost:8080/ account/services/accountService", name);
mapping.register(type, qName,
new BeanSerializerFactory(type, qName),
new BeanDeserializerFactory(type, qName));
}
}

Registering your own JAX-RPC Handler
Here we will register our own javax.rpc.xml.handler.Handler to the web service proxy where we can do custom code before the SOAP message is sent over the wire. The Handler is a callback interface. There is a convenience base class provided in jaxrpc.jar, namelyjavax.rpc.xml.handler.GenericHandler that we will extend:

public class AccountHandler extends GenericHandler {
public QName[] getHeaders() {
return null;
}
public boolean handleRequest(MessageContext context) {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage msg = smc.getMessage();
try {
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.getHeader();
...
}
catch (SOAPException ex) {
throw new JAXRPCException(ex);
}
return true;
}
}

What we need to do now is to register our AccountHandler to JAX-RPC Service so it would invoke handleRequest(..) before the message is sent over the wire. Spring has at this time of writing no declarative support for registering handlers, so we must use the programmatic approach. However Spring has made it very easy for us to do this as we can override the postProcessJaxRpcService(..) method that is designed for this:

public class AccountHandlerJaxRpcPortProxyFactoryBeanextends JaxRpcPortProxyFactoryBean {
protected void postProcessJaxRpcService(Service service) {
QName port = new QName(this.getNamespaceUri(), this.getPortName());
List list = service.getHandlerRegistry().getHandlerChain(port);
list.add(new HandlerInfo(AccountHandler.class, null, null));
logger.info("Registered JAX-RPC AccountHandler on port " + port);
}
}

The last thing we must remember to do is to change the Spring configuration to use our factory bean:

<bean id="accountWebService" class="example. AccountHandlerJaxRpcPortProxyFactoryBean">
...
</bean>

Exposing servlet-based web services using JAX-WS
Spring provides a convenient base class for JAX-WS servlet endpoint implementations -
SpringBeanAutowiringSupport. To expose our AccountService we extend Spring's Spring BeanAutowiringSupport class and implement our business logic here, usually delegating the call to the business layer. We'll simply use Spring 2.5's @Autowired annotation for expressing such dependencies on Spring-managed beans.

/**
* JAX-WS compliant AccountService implementation that simply delegates
* to the AccountService implementation in the root web application context.
*
* This wrapper class is necessary because JAX-WS
requires working with dedicated

* endpoint classes. If an existing service
needs to be exported, a wrapper that

* extends SpringBeanAutowiringSupport for
simple Spring bean autowiring (through

* the @Autowired annotation) is the
simplest JAX-WS compliant way.

*
* This is the class registered with
the server-side JAX-WS implementation.

* In the case of a Java EE 5 server,
this would simply be defined as a servlet

* in web.xml, with the server detecting that this
is a JAX-WS endpoint and reacting

* accordingly. The servlet name usually
needs to match the specified WS service name.

*
* The web service engine manages the
lifecycle of instances of this class.

* Spring bean references will just be
wired in here.

*/
import org.springframework.web.context. upport.SpringBeanAutowiringSupport;
@WebService(serviceName="AccountService")
public class AccountServiceEndpoint extends SpringBeanAutowiringSupport {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public Account[] getAccounts(String name) {
return biz.getAccounts(name);
}
}

Our AccountServletEndpoint needs to run in the same web application as the Spring context to allow for access to Spring's facilities. This is the case by default in Java EE 5 environments, using the standard contract for JAX-WS servlet endpoint deployment. See Java EE 5 web service tutorials for details.

Exporting standalone web services using JAX-WS
The built-in JAX-WS provider that comes with Sun's JDK 1.6 supports exposure of web services using the built-in HTTP server that's included in JDK 1.6 as well. Spring's SimpleJaxWsServiceExporter detects all @WebService annotated beans in the Spring application context, exporting them through the default JAX-WS server (the JDK 1.6 HTTP server).

In this scenario, the endpoint instances are defined and managed as Spring beans themselves; they will be registered with the JAX-WS engine but their lifecycle will be up to the Spring application context. This means that Spring functionality like explicit dependency injection may be applied to the endpoint instances. Of course, annotation-driven injection through @Autowired will work as well.

<bean class="org.springframework.remoting. jaxws.SimpleJaxWsServiceExporter">
<property name="baseAddress" value="http://localhost:8080/"/>
</bean>
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
...
</bean>
...

The AccountServiceEndpoint may derive from Spring's SpringBeanAutowiringSupport but doesn't have to since the endpoint is a fully Spring-managed bean here. This means that the endpoint implementation may look like as follows, without any superclass declared - and Spring's @Autowired configuration annotation still being honored:

@WebService(serviceName="AccountService")
public class AccountServiceEndpoint {
@Autowired
private AccountService biz;
@WebMethod
public void insertAccount(Account acc) {
biz.insertAccount(acc);
}
@WebMethod
public List<Account> getAccounts(String name) {
return biz.getAccounts(name);
}
}

Exporting web services using the JAX-WS RI's Spring support
Sun's JAX-WS RI, developed as part of the GlassFish project, ships Spring support as part of its JAX-WS Commons project. This allows for defining JAX-WS endpoints as Spring-managed beans - but this time in a Servlet environment. Note that thisis not portable in a Java EE 5 environment; it is mainly intended for non-EE environments such asTomcat, embedding the JAX-WS RI as part of the web application.

The difference to the standard style of exporting servlet-based endpoints is that the lifecycle of the endpoint instances themselves will be managed by Spring here, and that there will be only one JAX-WS servlet defined in web.xml. With the standard Java EE 5 style (as illustrated above), you'll have one servlet definition per service endpoint, with each endpoint typically delegating to Spring beans (through the use of @Autowired, as shown above).

Accessing web services using JAX-WS
Analogous to the JAX-RPC support, Spring provides two factory beans to create JAX-WS web service proxies, namely LocalJaxWs ServiceFactoryBean and JaxWsPortProxy FactoryBean. The former can only return a JAX-WS service class for us to work with. The latter is the full-fledged version that can return a proxy that implements our business service interface. In this example we use the latter to create a proxy for the AccountService endpoint (again):

<bean id="accountWebService" class="org. springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="example.AccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8888/Account ServiceEndpoint?WSDL"/>
<property name="namespaceUri" value="http://example/"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountServiceEndpointPort"/>
</bean>

Where serviceInterface is our business interface the clients will use. wsdlDocumentUrl is the URL for the WSDL file. Spring needs this a startup time to create the JAX-WS Service.

namespaceUri corresponds to the targetNamespace in the .wsdl file. serviceName corresponds to the service name in the .wsdl file. portName corresponds to the port name in the .wsdl file.

Accessing the web service is now very easy as we have a bean factory for it that will expose it as AccountService interface. We can wire this up in Spring:

<bean id="client" class="example.AccountClientImpl">
...
<property name="service" ref="accountWebService"/>
</bean> From the client code we can access the web service just as if it was a normal class:
public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}

NOTE: The above is slightly simplified in that JAX-WS requires endpoint interfaces and implementation classes to be annotated with @WebService, @SOAPBinding etc annotations. This means that you cannot (easily) use plain Java interfaces and implementation classes as JAX-WS endpoint artifacts; you need to annotate them accordingly first. Check the JAX-WS documentation for details on those requirements.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Java-Springs Topics