Removed JAX-RPC support

This commit is contained in:
Juergen Hoeller
2013-03-19 14:57:49 +01:00
parent 9c52ae9558
commit f1258a6a02
10 changed files with 3 additions and 2440 deletions

View File

@@ -51,11 +51,6 @@
<classname>BurlapServiceExporter</classname>.</para>
</listitem>
<listitem>
<para><emphasis>JAX-RPC</emphasis>. Spring provides remoting support
for web services via JAX-RPC (J2EE 1.4's web service API).</para>
</listitem>
<listitem>
<para><emphasis>JAX-WS</emphasis>. Spring provides remoting support
for web services via JAX-WS (the successor of JAX-RPC, as introduced
@@ -501,14 +496,6 @@ public class AccountServiceImpl implements AccountService {
APIs:</para>
<itemizedlist>
<listitem>
<para>Exposing web services using JAX-RPC</para>
</listitem>
<listitem>
<para>Accessing web services using JAX-RPC</para>
</listitem>
<listitem>
<para>Exposing web services using JAX-WS</para>
</listitem>
@@ -518,285 +505,13 @@ public class AccountServiceImpl implements AccountService {
</listitem>
</itemizedlist>
<note>
<para>Why two standard Java web services APIs?</para>
<para>JAX-RPC 1.1 is the standard web service API in J2EE 1.4. As its
name indicates, it focuses on on RPC bindings, which became less and
less popular in the past couple of years. As a consequence, it has been
superseded by JAX-WS 2.0 in Java EE 5, being more flexible in terms of
bindings but also being heavily annotation-based. JAX-WS 2.1 is also
included in Java 6 (or more specifically, in Sun's JDK 1.6.0_04 and
above; previous Sun JDK 1.6.0 releases included JAX-WS 2.0), integrated
with the JDK's built-in HTTP server.</para>
<para>Spring can work with both standard Java web services APIs. On Java
EE 5 / Java 6, the obvious choice is JAX-WS. On J2EE 1.4 environments
that run on Java 5, you might have the option to plug in a JAX-WS
provider; check your Java EE server's documentation.</para>
</note>
<para>In addition to stock support for JAX-RPC and JAX-WS in Spring Core,
<para>In addition to stock support for JAX-WS in Spring Core,
the Spring portfolio also features <link
xl:href="http://www.springframework.org/spring-ws">Spring Web
Services</link>, a solution for contract-first, document-driven web
services - highly recommended for building modern, future-proof web
services.</para>
<section xml:id="remoting-web-services-jaxrpc-export">
<title>Exposing servlet-based web services using JAX-RPC</title>
<para>Spring provides a convenience base class for JAX-RPC servlet
endpoint implementations -
<classname>ServletEndpointSupport</classname>. To expose our
<interfacename>AccountService</interfacename> we extend Spring's
<classname>ServletEndpointSupport</classname> class and implement our
business logic here, usually delegating the call to the business
layer.</para>
<programlisting language="java"><lineannotation>/**
* 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.
*/</lineannotation>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);
}
}</programlisting>
<para>Our <classname>AccountServletEndpoint</classname> 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
<classname>AxisServlet</classname> definition into your
<filename>'web.xml'</filename>, and set up the endpoint in
<filename>'server-config.wsdd'</filename> (or use the deploy tool). See
the sample application JPetStore where the
<interfacename>OrderService</interfacename> is exposed as a web service
using Axis.</para>
</section>
<section xml:id="remoting-web-services-jaxrpc-access">
<title>Accessing web services using JAX-RPC</title>
<para>Spring provides two factory beans to create JAX-RPC web service
proxies, namely <classname>LocalJaxRpcServiceFactoryBean</classname> and
<classname>JaxRpcPortProxyFactoryBean</classname>. 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. In this example we use the latter to create
a proxy for the <interfacename>AccountService</interfacename> endpoint
we exposed in the previous section. 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:</para>
<programlisting language="xml">&lt;bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"&gt;
&lt;property name="serviceInterface" value="example.RemoteAccountService"/&gt;
&lt;property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/&gt;
&lt;property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/&gt;
&lt;property name="serviceName" value="AccountService"/&gt;
&lt;property name="portName" value="AccountPort"/&gt;
&lt;/bean&gt;</programlisting>
<para>Where <literal>serviceInterface</literal> is our remote business
interface the clients will use. <literal>wsdlDocumentUrl</literal> is
the URL for the WSDL file. Spring needs this at startup time to create
the JAX-RPC Service. <literal>namespaceUri</literal> corresponds to the
targetNamespace in the .wsdl file. <literal>serviceName</literal>
corresponds to the service name in the .wsdl file.
<literal>portName</literal> corresponds to the port name in the .wsdl
file.</para>
<para>Accessing the web service is now very easy as we have a bean
factory for it that will expose it as
<literal>RemoteAccountService</literal> interface. We can wire this up
in Spring:</para>
<programlisting language="xml">&lt;bean id="client" class="example.AccountClientImpl"&gt;
...
&lt;property name="service" ref="accountWebService"/&gt;
&lt;/bean&gt;</programlisting>
<para>From the client code we can access the web service just as if it
was a normal class, except that it throws
<exceptionname>RemoteException</exceptionname>.</para>
<programlisting language="java">public class AccountClientImpl {
private RemoteAccountService service;
public void setService(RemoteAccountService service) {
this.service = service;
}
public void foo() {
try {
service.insertAccount(...);
}
catch (RemoteException ex) {
<lineannotation>// ouch</lineannotation>
}
}
}
</programlisting>
<para>We can get rid of the checked
<exceptionname>RemoteException</exceptionname> since Spring supports
automatic conversion to its corresponding unchecked
<exceptionname>RemoteException</exceptionname>. This requires that we
provide a non-RMI interface also. Our configuration is now:</para>
<programlisting language="xml">&lt;bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"&gt;
&lt;property name="serviceInterface" value="example.AccountService"/&gt;
&lt;property name="portInterface" value="example.RemoteAccountService"/&gt;
...
&lt;/bean&gt;</programlisting>
<para>Where <literal>serviceInterface</literal> is changed to our non
RMI interface. Our RMI interface is now defined using the property
<literal>portInterface</literal>. Our client code can now avoid handling
<exceptionname>java.rmi.RemoteException</exceptionname>:</para>
<programlisting language="java">public class AccountClientImpl {
private AccountService service;
public void setService(AccountService service) {
this.service = service;
}
public void foo() {
service.insertAccount(...);
}
}</programlisting>
<para>Note that you can also drop the "portInterface" part and specify a
plain business interface as "serviceInterface". In this case,
<classname>JaxRpcPortProxyFactoryBean</classname> 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
<classname>JaxRpcPortProxyFactoryBean</classname>'s javadoc for details
on the runtime implications.</para>
</section>
<section xml:id="remoting-web-services-jaxrpc-mapping-registration">
<title>Registering JAX-RPC Bean Mappings</title>
<para>To transfer complex objects over the wire such as
<classname>Account</classname> we must register bean mappings on the
client side.</para>
<note>
<para>On the server side using Axis registering bean mappings is
usually done in the <filename>'server-config.wsdd'</filename>
file.</para>
</note>
<para>We will use Axis to register bean mappings on the client side. To
do this we need to register the bean mappings programmatically:</para>
<programlisting language="java">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));
}
}</programlisting>
</section>
<section xml:id="remoting-web-services-jaxrpc-handler-registration">
<title>Registering your own JAX-RPC Handler</title>
<para>In this section we will register our own
<interfacename>javax.rpc.xml.handler.Handler</interfacename> to the web
service proxy where we can do custom code before the SOAP message is
sent over the wire. The <interfacename>Handler</interfacename> is a
callback interface. There is a convenience base class provided in
<filename class="libraryfile">jaxrpc.jar</filename>, namely
<classname>javax.rpc.xml.handler.GenericHandler</classname> that we will
extend:</para>
<programlisting language="java">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;
}
}</programlisting>
<para>What we need to do now is to register our AccountHandler to
JAX-RPC Service so it would invoke
<methodname>handleRequest(..)</methodname> 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 <methodname>postProcessJaxRpcService(..)</methodname>
method that is designed for this:</para>
<programlisting language="java">public class AccountHandlerJaxRpcPortProxyFactoryBean extends 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);
}
}</programlisting>
<para>The last thing we must remember to do is to change the Spring
configuration to use our factory bean:</para>
<programlisting language="xml">&lt;bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean"&gt;
...
&lt;/bean&gt;</programlisting>
</section>
<section xml:id="remoting-web-services-jaxws-export-servlet">
<title>Exposing servlet-based web services using JAX-WS</title>
@@ -937,9 +652,8 @@ public class AccountServiceEndpoint {
<section xml:id="remoting-web-services-jaxws-access">
<title>Accessing web services using JAX-WS</title>
<para>Analogous to the JAX-RPC support, Spring provides two factory
beans to create JAX-WS web service proxies, namely
<classname>LocalJaxWsServiceFactoryBean</classname> and
<para>Spring provides two factory beans to create JAX-WS web service
proxies, namely <classname>LocalJaxWsServiceFactoryBean</classname> and
<classname>JaxWsPortProxyFactoryBean</classname>. 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