1606 lines
73 KiB
XML
1606 lines
73 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
|
<chapter id="remoting">
|
|
<title>Remoting and web services using Spring</title>
|
|
|
|
<section id="remoting-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>Spring features integration classes for remoting support using
|
|
various technologies. The remoting support eases the development of
|
|
remote-enabled services, implemented by your usual (Spring) POJOs.
|
|
Currently, Spring supports four remoting technologies: <itemizedlist>
|
|
<listitem>
|
|
<para><emphasis>Remote Method Invocation (RMI)</emphasis>. Through
|
|
the use of the <classname>RmiProxyFactoryBean</classname> and the
|
|
<classname>RmiServiceExporter</classname> Spring supports both
|
|
traditional RMI (with <interfacename>java.rmi.Remote</interfacename>
|
|
interfaces and
|
|
<exceptionname>java.rmi.RemoteException</exceptionname>) and
|
|
transparent remoting via RMI invokers (with any Java
|
|
interface).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>Spring's HTTP invoker</emphasis>. Spring provides a
|
|
special remoting strategy which allows for Java serialization via
|
|
HTTP, supporting any Java interface (just like the RMI invoker). The
|
|
corresponding support classes are
|
|
<classname>HttpInvokerProxyFactoryBean</classname> and
|
|
<classname>HttpInvokerServiceExporter</classname>.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>Hessian</emphasis>. By using Spring's
|
|
<classname>HessianProxyFactoryBean</classname> and the
|
|
<classname>HessianServiceExporter</classname> you can transparently
|
|
expose your services using the lightweight binary HTTP-based
|
|
protocol provided by Caucho.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>Burlap</emphasis>. Burlap is Caucho's XML-based
|
|
alternative to Hessian. Spring provides support classes such as
|
|
<classname>BurlapProxyFactoryBean</classname> and
|
|
<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
|
|
in Java EE 5 and Java 6).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>JMS</emphasis>. Remoting using JMS as the underlying
|
|
protocol is supported via the
|
|
<classname>JmsInvokerServiceExporter</classname> and
|
|
<classname>JmsInvokerProxyFactoryBean</classname> classes.</para>
|
|
</listitem>
|
|
</itemizedlist></para>
|
|
|
|
<para>While discussing the remoting capabilities of Spring, we'll use the
|
|
following domain model and corresponding services:</para>
|
|
|
|
<programlisting language="java">public class Account implements Serializable{
|
|
|
|
private String name;
|
|
|
|
public String getName(){
|
|
return name;
|
|
}
|
|
|
|
public void setName(String name) {
|
|
this.name = name;
|
|
}
|
|
}</programlisting>
|
|
|
|
<programlisting language="java">public interface AccountService {
|
|
|
|
public void insertAccount(Account account);
|
|
|
|
public List<Account> getAccounts(String name);
|
|
}</programlisting>
|
|
|
|
<programlisting language="java">public interface RemoteAccountService extends Remote {
|
|
|
|
public void insertAccount(Account account) throws RemoteException;
|
|
|
|
public List<Account> getAccounts(String name) throws RemoteException;
|
|
}</programlisting>
|
|
|
|
<programlisting language="java"><lineannotation>// the implementation doing nothing at the moment</lineannotation>
|
|
public class AccountServiceImpl implements AccountService {
|
|
|
|
public void insertAccount(Account acc) {
|
|
<lineannotation>// do something...</lineannotation>
|
|
}
|
|
|
|
public List<Account> getAccounts(String name) {
|
|
<lineannotation>// do something...</lineannotation>
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>We will start exposing the service to a remote client by using RMI
|
|
and talk a bit about the drawbacks of using RMI. We'll then continue to
|
|
show an example using Hessian as the protocol.</para>
|
|
</section>
|
|
|
|
<section id="remoting-rmi">
|
|
<title>Exposing services using RMI</title>
|
|
|
|
<para>Using Spring's support for RMI, you can transparently expose your
|
|
services through the RMI infrastructure. After having this set up, you
|
|
basically have a configuration similar to remote EJBs, except for the fact
|
|
that there is no standard support for security context propagation or
|
|
remote transaction propagation. Spring does provide hooks for such
|
|
additional invocation context when using the RMI invoker, so you can for
|
|
example plug in security frameworks or custom security credentials
|
|
here.</para>
|
|
|
|
<section id="remoting-rmi-server">
|
|
<title>Exporting the service using the
|
|
<classname>RmiServiceExporter</classname></title>
|
|
|
|
<para>Using the <classname>RmiServiceExporter</classname>, we can expose
|
|
the interface of our AccountService object as RMI object. The interface
|
|
can be accessed by using <classname>RmiProxyFactoryBean</classname>, or
|
|
via plain RMI in case of a traditional RMI service. The
|
|
<classname>RmiServiceExporter</classname> explicitly supports the
|
|
exposing of any non-RMI services via RMI invokers.</para>
|
|
|
|
<para>Of course, we first have to set up our service in the Spring
|
|
container:</para>
|
|
|
|
<programlisting language="xml"><bean id="accountService" class="example.AccountServiceImpl">
|
|
<lineannotation><!-- any additional properties, maybe a DAO? --></lineannotation>
|
|
</bean></programlisting>
|
|
|
|
<para>Next we'll have to expose our service using the
|
|
<classname>RmiServiceExporter</classname>:</para>
|
|
|
|
<programlisting language="xml"><bean class="org.springframework.remoting.rmi.RmiServiceExporter">
|
|
<lineannotation><!-- does not necessarily have to be the same name as the bean to be exported --></lineannotation>
|
|
<property name="serviceName" value="AccountService"/>
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
<lineannotation><!-- defaults to <literal>1099</literal> --></lineannotation>
|
|
<property name="registryPort" value="1199"/>
|
|
</bean></programlisting>
|
|
|
|
<para>As you can see, we're overriding the port for the RMI registry.
|
|
Often, your application server also maintains an RMI registry and it is
|
|
wise to not interfere with that one. Furthermore, the service name is
|
|
used to bind the service under. So right now, the service will be bound
|
|
at <literal>'rmi://HOST:1199/AccountService'</literal>. We'll use the
|
|
URL later on to link in the service at the client side.</para>
|
|
|
|
<note>
|
|
<para>The <literal>servicePort</literal> property has been omitted (it
|
|
defaults to 0). This means that an anonymous port will be used to
|
|
communicate with the service.</para>
|
|
</note>
|
|
</section>
|
|
|
|
<section id="remoting-rmi-client">
|
|
<title>Linking in the service at the client</title>
|
|
|
|
<para>Our client is a simple object using the
|
|
<interfacename>AccountService</interfacename> to manage accounts:</para>
|
|
|
|
<programlisting language="java">public class SimpleObject {
|
|
|
|
private AccountService accountService;
|
|
|
|
public void setAccountService(AccountService accountService) {
|
|
this.accountService = accountService;
|
|
}
|
|
|
|
// additional methods using the accountService
|
|
|
|
}</programlisting>
|
|
|
|
<para>To link in the service on the client, we'll create a separate
|
|
Spring container, containing the simple object and the service linking
|
|
configuration bits:</para>
|
|
|
|
<programlisting language="xml"><bean class="example.SimpleObject">
|
|
<property name="accountService" ref="accountService"/>
|
|
</bean>
|
|
|
|
<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
|
|
<property name="serviceUrl" value="rmi://HOST:1199/AccountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean></programlisting>
|
|
|
|
<para>That's all we need to do to support the remote account service on
|
|
the client. Spring will transparently create an invoker and remotely
|
|
enable the account service through the
|
|
<classname>RmiServiceExporter</classname>. At the client we're linking
|
|
it in using the <classname>RmiProxyFactoryBean</classname>.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="remoting-caucho-protocols">
|
|
<title>Using Hessian or Burlap to remotely call services via HTTP</title>
|
|
|
|
<para>Hessian offers a binary HTTP-based remoting protocol. It is
|
|
developed by Caucho and more information about Hessian itself can be found
|
|
at <ulink url="http://www.caucho.com"></ulink>.</para>
|
|
|
|
<section id="remoting-caucho-protocols-hessian">
|
|
<title>Wiring up the <classname>DispatcherServlet</classname> for
|
|
Hessian and co.</title>
|
|
|
|
<para>Hessian communicates via HTTP and does so using a custom servlet.
|
|
Using Spring's <classname>DispatcherServlet</classname> principles, as
|
|
known from Spring Web MVC usage, you can easily wire up such a servlet
|
|
exposing your services. First we'll have to create a new servlet in your
|
|
application (this an excerpt from
|
|
<filename>'web.xml'</filename>):</para>
|
|
|
|
<programlisting language="xml"><servlet>
|
|
<servlet-name>remoting</servlet-name>
|
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
|
<load-on-startup>1</load-on-startup>
|
|
</servlet>
|
|
|
|
<servlet-mapping>
|
|
<servlet-name>remoting</servlet-name>
|
|
<url-pattern>/remoting/*</url-pattern>
|
|
</servlet-mapping></programlisting>
|
|
|
|
<para>You're probably familiar with Spring's
|
|
<classname>DispatcherServlet</classname> principles and if so, you know
|
|
that now you'll have to create a Spring container configuration resource
|
|
named <filename>'remoting-servlet.xml'</filename> (after the name of
|
|
your servlet) in the <filename class="directory">'WEB-INF'</filename>
|
|
directory. The application context will be used in the next
|
|
section.</para>
|
|
|
|
<para>Alternatively, consider the use of Spring's simpler
|
|
<classname>HttpRequestHandlerServlet</classname>. This allows you to
|
|
embed the remote exporter definitions in your root application context
|
|
(by default in <filename>'WEB-INF/applicationContext.xml'</filename>),
|
|
with individual servlet definitions pointing to specific exporter beans.
|
|
Each servlet name needs to match the bean name of its target exporter in
|
|
this case.</para>
|
|
</section>
|
|
|
|
<section id="remoting-caucho-protocols-hessian-server">
|
|
<title>Exposing your beans by using the
|
|
<classname>HessianServiceExporter</classname></title>
|
|
|
|
<para>In the newly created application context called
|
|
<literal>remoting-servlet.xml</literal>, we'll create a
|
|
<classname>HessianServiceExporter</classname> exporting your
|
|
services:</para>
|
|
|
|
<programlisting language="xml"><bean id="accountService" class="example.AccountServiceImpl">
|
|
<lineannotation><!-- any additional properties, maybe a DAO? --></lineannotation>
|
|
</bean>
|
|
|
|
<bean name="/AccountService" class="org.springframework.remoting.caucho.HessianServiceExporter">
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean></programlisting>
|
|
|
|
<para>Now we're ready to link in the service at the client. No explicit
|
|
handler mapping is specified, mapping request URLs onto services, so
|
|
<classname>BeanNameUrlHandlerMapping</classname> will be used: Hence,
|
|
the service will be exported at the URL indicated through its bean name
|
|
within the containing <classname>DispatcherServlet</classname>'s mapping
|
|
(as defined above):
|
|
<literal>'http://HOST:8080/remoting/AccountService'</literal>.</para>
|
|
|
|
<para>Alternatively, create a
|
|
<classname>HessianServiceExporter</classname> in your root application
|
|
context (e.g. in
|
|
<filename>'WEB-INF/applicationContext.xml'</filename>):</para>
|
|
|
|
<programlisting language="xml"><bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean></programlisting>
|
|
|
|
<para>In the latter case, define a corresponding servlet for this
|
|
exporter in <filename>'web.xml'</filename>, with the same end result:
|
|
The exporter getting mapped to the request path
|
|
<literal>/remoting/AccountService</literal>. Note that the servlet name
|
|
needs to match the bean name of the target exporter.</para>
|
|
|
|
<programlisting language="xml"><servlet>
|
|
<servlet-name>accountExporter</servlet-name>
|
|
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
|
|
</servlet>
|
|
|
|
<servlet-mapping>
|
|
<servlet-name>accountExporter</servlet-name>
|
|
<url-pattern>/remoting/AccountService</url-pattern>
|
|
</servlet-mapping></programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-caucho-protocols-hessian-client">
|
|
<title>Linking in the service on the client</title>
|
|
|
|
<para>Using the <classname>HessianProxyFactoryBean</classname> we can
|
|
link in the service at the client. The same principles apply as with the
|
|
RMI example. We'll create a separate bean factory or application context
|
|
and mention the following beans where the
|
|
<classname>SimpleObject</classname> is using the
|
|
<interfacename>AccountService</interfacename> to manage accounts:</para>
|
|
|
|
<programlisting language="xml"><bean class="example.SimpleObject">
|
|
<property name="accountService" ref="accountService"/>
|
|
</bean>
|
|
|
|
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
|
|
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean></programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-caucho-protocols-burlap">
|
|
<title>Using Burlap</title>
|
|
|
|
<para>We won't discuss Burlap, the XML-based equivalent of Hessian, in
|
|
detail here, since it is configured and set up in exactly the same way
|
|
as the Hessian variant explained above. Just replace the word
|
|
<literal>Hessian</literal> with <literal>Burlap</literal> and you're all
|
|
set to go.</para>
|
|
</section>
|
|
|
|
<section id="remoting-caucho-protocols-security">
|
|
<title>Applying HTTP basic authentication to a service exposed through
|
|
Hessian or Burlap</title>
|
|
|
|
<para>One of the advantages of Hessian and Burlap is that we can easily
|
|
apply HTTP basic authentication, because both protocols are HTTP-based.
|
|
Your normal HTTP server security mechanism can easily be applied through
|
|
using the <literal>web.xml</literal> security features, for example.
|
|
Usually, you don't use per-user security credentials here, but rather
|
|
shared credentials defined at the
|
|
<literal>Hessian/BurlapProxyFactoryBean</literal> level (similar to a
|
|
JDBC <interfacename>DataSource</interfacename>).</para>
|
|
|
|
<programlisting language="xml"><bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
|
|
<property name="interceptors" ref="authorizationInterceptor"/>
|
|
</bean>
|
|
|
|
<bean id="authorizationInterceptor"
|
|
class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor">
|
|
<property name="authorizedRoles" value="administrator,operator"/>
|
|
</bean></programlisting>
|
|
|
|
<para>This an example where we explicitly mention the
|
|
<classname>BeanNameUrlHandlerMapping</classname> and set an interceptor
|
|
allowing only administrators and operators to call the beans mentioned
|
|
in this application context.</para>
|
|
|
|
<note>
|
|
<para>Of course, this example doesn't show a flexible kind of security
|
|
infrastructure. For more options as far as security is concerned, have
|
|
a look at the Acegi Security System for Spring, to be found at <ulink
|
|
url="http://acegisecurity.sourceforge.net"></ulink>.</para>
|
|
</note>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="remoting-httpinvoker">
|
|
<title>Exposing services using HTTP invokers</title>
|
|
|
|
<para>As opposed to Burlap and Hessian, which are both lightweight
|
|
protocols using their own slim serialization mechanisms, Spring Http
|
|
invokers use the standard Java serialization mechanism to expose services
|
|
through HTTP. This has a huge advantage if your arguments and return types
|
|
are complex types that cannot be serialized using the serialization
|
|
mechanisms Hessian and Burlap use (refer to the next section for more
|
|
considerations when choosing a remoting technology).</para>
|
|
|
|
<para>Under the hood, Spring uses either the standard facilities provided
|
|
by J2SE to perform HTTP calls or Commons
|
|
<classname>HttpClient</classname>. Use the latter if you need more
|
|
advanced and easy-to-use functionality. Refer to <ulink
|
|
url="http://jakarta.apache.org/commons/httpclient">jakarta.apache.org/commons/httpclient</ulink>
|
|
for more info.</para>
|
|
|
|
<section id="remoting-httpinvoker-server">
|
|
<title>Exposing the service object</title>
|
|
|
|
<para>Setting up the HTTP invoker infrastructure for a service objects
|
|
much resembles the way you would do using Hessian or Burlap. Just as
|
|
Hessian support provides the
|
|
<classname>HessianServiceExporter</classname>, Spring's HttpInvoker
|
|
support provides the
|
|
<classname>org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter</classname>.</para>
|
|
|
|
<para>To expose the <literal>AccountService</literal> (mentioned above)
|
|
within a Spring Web MVC <classname>DispatcherServlet</classname>, the
|
|
following configuration needs to be in place in the dispatcher's
|
|
application context:</para>
|
|
|
|
<programlisting language="xml"><bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean>
|
|
</programlisting>
|
|
|
|
<para>Such an exporter definition will be exposed through the
|
|
<classname>DispatcherServlet</classname>'s standard mapping facilities,
|
|
as explained in the section on Hessian.</para>
|
|
|
|
<para>Alternatively, create an
|
|
<classname>HttpInvokerServiceExporter</classname> in your root
|
|
application context (e.g. in
|
|
<filename>'WEB-INF/applicationContext.xml'</filename>):</para>
|
|
|
|
<programlisting language="xml"><bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean></programlisting>
|
|
|
|
<para>In addition, define a corresponding servlet for this exporter in
|
|
<filename>'web.xml'</filename>, with the servlet name matching the bean
|
|
name of the target exporter:</para>
|
|
|
|
<programlisting language="xml"><servlet>
|
|
<servlet-name>accountExporter</servlet-name>
|
|
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
|
|
</servlet>
|
|
|
|
<servlet-mapping>
|
|
<servlet-name>accountExporter</servlet-name>
|
|
<url-pattern>/remoting/AccountService</url-pattern>
|
|
</servlet-mapping></programlisting>
|
|
|
|
<para>f you are running outside of a servlet container and are using
|
|
Sun's Java 6, then you can use the built-in HTTP server implementation.
|
|
You can configure the SimpleHttpServerFactoryBean together with a
|
|
SimpleHttpInvokerServiceExporter as is shown in this example:</para>
|
|
|
|
<programlisting language="xml"><bean name="accountExporter"
|
|
class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
|
|
<property name="service" ref="accountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean>
|
|
|
|
<bean id="httpServer"
|
|
class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
|
|
<property name="contexts">
|
|
<util:map>
|
|
<entry key="/remoting/AccountService" value-ref="accountExporter"/>
|
|
</util:map>
|
|
</property>
|
|
<property name="port" value="8080" />
|
|
</bean>
|
|
</programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-httpinvoker-client">
|
|
<title>Linking in the service at the client</title>
|
|
|
|
<para>Again, linking in the service from the client much resembles the
|
|
way you would do it when using Hessian or Burlap. Using a proxy, Spring
|
|
will be able to translate your calls to HTTP POST requests to the URL
|
|
pointing to the exported service.</para>
|
|
|
|
<programlisting language="xml"><bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
|
|
<property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/>
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
</bean>
|
|
</programlisting>
|
|
|
|
<para>As mentioned before, you can choose what HTTP client you want to
|
|
use. By default, the <classname>HttpInvokerProxy</classname> uses the
|
|
J2SE HTTP functionality, but you can also use the Commons
|
|
<classname>HttpClient</classname> by setting the
|
|
<literal>httpInvokerRequestExecutor</literal> property:</para>
|
|
|
|
<programlisting language="xml"><property name="httpInvokerRequestExecutor">
|
|
<bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/>
|
|
</property>
|
|
</programlisting>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="remoting-web-services">
|
|
<title>Web services</title>
|
|
|
|
<para>Spring provides full support for standard Java web services
|
|
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>
|
|
|
|
<listitem>
|
|
<para>Accessing web services using JAX-WS</para>
|
|
</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,
|
|
the Spring portfolio also features <ulink
|
|
url="http://www.springframework.org/spring-ws">Spring Web
|
|
Services</ulink>, a solution for contract-first, document-driven web
|
|
services - highly recommended for building modern, future-proof web
|
|
services. Last but not least, <ulink
|
|
url="http://xfire.codehaus.org">XFire</ulink> also allows you to export
|
|
Spring-managed beans as a web service, through built-in Spring
|
|
support.</para>
|
|
|
|
<section 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 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"><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></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 a 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"><bean id="client" class="example.AccountClientImpl">
|
|
...
|
|
<property name="service" ref="accountWebService"/>
|
|
</bean></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"><bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
<property name="portInterface" value="example.RemoteAccountService"/>
|
|
...
|
|
</bean></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 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 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"><bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean">
|
|
...
|
|
</bean></programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-web-services-jaxws-export-servlet">
|
|
<title>Exposing servlet-based web services using JAX-WS</title>
|
|
|
|
<para>Spring provides a convenient base class for JAX-WS servlet
|
|
endpoint implementations -
|
|
<classname>SpringBeanAutowiringSupport</classname>. To expose our
|
|
<interfacename>AccountService</interfacename> we extend Spring's
|
|
<classname>SpringBeanAutowiringSupport</classname> class and implement
|
|
our business logic here, usually delegating the call to the business
|
|
layer. We'll simply use Spring 2.5's <literal>@Autowired</literal>
|
|
annotation for expressing such dependencies on Spring-managed
|
|
beans.</para>
|
|
|
|
<programlisting language="java"><lineannotation>/**
|
|
* 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.
|
|
*/</lineannotation>
|
|
import org.springframework.web.context.support.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);
|
|
}
|
|
}</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. 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.</para>
|
|
</section>
|
|
|
|
<section id="remoting-web-services-jaxws-export-standalone">
|
|
<title>Exporting standalone web services using JAX-WS</title>
|
|
|
|
<para>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
|
|
<classname>SimpleJaxWsServiceExporter</classname> detects all
|
|
<literal>@WebService</literal> annotated beans in the Spring application
|
|
context, exporting them through the default JAX-WS server (the JDK 1.6
|
|
HTTP server).</para>
|
|
|
|
<para>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 <literal>@Autowired</literal> will work as
|
|
well.</para>
|
|
|
|
<programlisting language="xml"><bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
|
|
<property name="baseAddress" value="http://localhost:8080/"/>
|
|
</bean>
|
|
|
|
<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint">
|
|
...
|
|
</bean>
|
|
|
|
...</programlisting>
|
|
|
|
<para>The <classname>AccountServiceEndpoint</classname> may derive from
|
|
Spring's <classname>SpringBeanAutowiringSupport</classname> 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 <literal>@Autowired</literal>
|
|
configuration annotation still being honored:</para>
|
|
|
|
<programlisting language="java">@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);
|
|
}
|
|
}</programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-web-services-jaxws-export-ri">
|
|
<title>Exporting web services using the JAX-WS RI's Spring
|
|
support</title>
|
|
|
|
<para>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, similar to the
|
|
standalone mode discussed in the previous section - but this time in a
|
|
Servlet environment. <emphasis>Note that this is not portable in a Java
|
|
EE 5 environment; it is mainly intended for non-EE environments such as
|
|
Tomcat, embedding the JAX-WS RI as part of the web
|
|
application.</emphasis></para>
|
|
|
|
<para>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 <literal>web.xml</literal>. 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 <literal>@Autowired</literal>, as shown
|
|
above).</para>
|
|
|
|
<para>Check out <ulink
|
|
url="https://jax-ws-commons.dev.java.net/spring/">https://jax-ws-commons.dev.java.net/spring/</ulink>
|
|
for the details on setup and usage style.</para>
|
|
</section>
|
|
|
|
<section 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
|
|
<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
|
|
business service interface. In this example we use the latter to create
|
|
a proxy for the <interfacename>AccountService</interfacename> endpoint
|
|
(again):</para>
|
|
|
|
<programlisting language="xml"><bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
|
|
<property name="serviceInterface" value="example.AccountService"/>
|
|
<property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
|
|
<property name="namespaceUri" value="http://example/"/>
|
|
<property name="serviceName" value="AccountService"/>
|
|
<property name="portName" value="AccountServiceEndpointPort"/>
|
|
</bean></programlisting>
|
|
|
|
<para>Where <literal>serviceInterface</literal> is our business
|
|
interface the clients will use. <literal>wsdlDocumentUrl</literal> is
|
|
the URL for the WSDL file. Spring needs this a startup time to create
|
|
the JAX-WS 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>AccountService</literal>
|
|
interface. We can wire this up in Spring:</para>
|
|
|
|
<programlisting language="xml"><bean id="client" class="example.AccountClientImpl">
|
|
...
|
|
<property name="service" ref="accountWebService"/>
|
|
</bean></programlisting>
|
|
|
|
<para>From the client code we can access the web service just as if it
|
|
was a normal class:</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><emphasis>NOTE:</emphasis> The above is slightly simplified in
|
|
that JAX-WS requires endpoint interfaces and implementation classes to
|
|
be annotated with <literal>@WebService</literal>,
|
|
<literal>@SOAPBinding</literal> 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.</para>
|
|
</section>
|
|
|
|
<section id="remoting-web-services-xfire">
|
|
<title>Exposing web services using XFire</title>
|
|
|
|
<para>XFire is a lightweight SOAP library, hosted by Codehaus. Exposing
|
|
XFire is done using a XFire context that shipping with XFire itself in
|
|
combination with a RemoteExporter-style bean you have to add to your
|
|
<interfacename>WebApplicationContext</interfacename>. As with all
|
|
methods that allow you to expose service, you have to create a
|
|
<classname>DispatcherServlet</classname> with a corresponding
|
|
<interfacename>WebApplicationContext</interfacename> containing the
|
|
services you will be exposing:</para>
|
|
|
|
<programlisting language="xml"><servlet>
|
|
<servlet-name>xfire</servlet-name>
|
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
|
</servlet></programlisting>
|
|
|
|
<para>You also have to link in the XFire configuration. This is done by
|
|
adding a context file to the <literal>contextConfigLocations</literal>
|
|
context parameter picked up by the
|
|
<classname>ContextLoaderListener</classname> (or
|
|
<classname>ContextLoaderServlet</classname> for that matter).</para>
|
|
|
|
<programlisting language="xml"><context-param>
|
|
<param-name>contextConfigLocation</param-name>
|
|
<param-value>classpath:org/codehaus/xfire/spring/xfire.xml</param-value>
|
|
</context-param>
|
|
|
|
<listener>
|
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
|
</listener></programlisting>
|
|
|
|
<para>After you added a servlet mapping (mapping <literal>/*</literal>
|
|
to the XFire servlet declared above) you only have to add one extra bean
|
|
to expose the service using XFire. Add for example the following
|
|
configuration in your <filename>'xfire-servlet.xml'</filename>
|
|
file:</para>
|
|
|
|
<programlisting language="xml"><beans>
|
|
|
|
<bean name="/Echo" class="org.codehaus.xfire.spring.remoting.XFireExporter">
|
|
<property name="serviceInterface" value="org.codehaus.xfire.spring.Echo"/>
|
|
<property name="serviceBean">
|
|
<bean class="org.codehaus.xfire.spring.EchoImpl"/>
|
|
</property>
|
|
<lineannotation><!-- the XFire bean is defined in the <filename>xfire.xml</filename> file --></lineannotation>
|
|
<property name="xfire" ref="xfire"/>
|
|
</bean>
|
|
|
|
</beans></programlisting>
|
|
|
|
<para>XFire handles the rest. It introspects your service interface and
|
|
generates a WSDL from it. Parts of this documentation have been taken
|
|
from the XFire site; for more detailed information on XFire Spring
|
|
integration, navigate to <ulink
|
|
url="http://docs.codehaus.org/display/XFIRE/Spring">http://docs.codehaus.org/display/XFIRE/Spring</ulink>.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section id="remoting-jms">
|
|
<title>JMS</title>
|
|
|
|
<para>It is also possible to expose services transparently using JMS as
|
|
the underlying communication protocol. The JMS remoting support in the
|
|
Spring Framework is pretty basic - it sends and receives on the
|
|
<interfacename>same thread</interfacename> and in the <emphasis>same
|
|
non-transactional</emphasis> <interfacename>Session</interfacename>, and
|
|
as such throughput will be very implementation dependent.</para>
|
|
|
|
<para>The following interface is used on both the server and the client
|
|
side.</para>
|
|
|
|
<programlisting language="java">package com.foo;
|
|
|
|
public interface CheckingAccountService {
|
|
|
|
public void cancelAccount(Long accountId);
|
|
}</programlisting>
|
|
|
|
<para>The following simple implementation of the above interface is used
|
|
on the server-side.</para>
|
|
|
|
<programlisting language="java">package com.foo;
|
|
|
|
public class SimpleCheckingAccountService implements CheckingAccountService {
|
|
|
|
public void cancelAccount(Long accountId) {
|
|
System.out.println("Cancelling account [" + accountId + "]");
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>This configuration file contains the JMS-infrastructure beans that
|
|
are shared on both the client and server.</para>
|
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
|
|
|
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
|
|
<property name="brokerURL" value="tcp://ep-t43:61616"/>
|
|
</bean>
|
|
|
|
<bean id="queue" class="org.apache.activemq.command.ActiveMQQueue">
|
|
<constructor-arg value="mmm"/>
|
|
</bean>
|
|
|
|
</beans></programlisting>
|
|
|
|
<section id="remoting-jms-server">
|
|
<title>Server-side configuration</title>
|
|
|
|
<para>On the server, you just need to expose the service object using
|
|
the <classname>JmsInvokerServiceExporter</classname>.</para>
|
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
|
|
|
<bean id="checkingAccountService"
|
|
class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
|
|
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
|
|
<property name="service">
|
|
<bean class="com.foo.SimpleCheckingAccountService"/>
|
|
</property>
|
|
</bean>
|
|
|
|
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
|
|
<property name="connectionFactory" ref="connectionFactory"/>
|
|
<property name="destination" ref="queue"/>
|
|
<property name="concurrentConsumers" value="3"/>
|
|
<property name="messageListener" ref="checkingAccountService"/>
|
|
</bean>
|
|
|
|
</beans></programlisting>
|
|
|
|
<programlisting language="java">package com.foo;
|
|
|
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|
|
|
public class Server {
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"});
|
|
}
|
|
}</programlisting>
|
|
</section>
|
|
|
|
<section id="remoting-jms-client">
|
|
<title>Client-side configuration</title>
|
|
|
|
<para>The client merely needs to create a client-side proxy that will
|
|
implement the agreed upon interface
|
|
(<interfacename>CheckingAccountService</interfacename>). The resulting
|
|
object created off the back of the following bean definition can be
|
|
injected into other client side objects, and the proxy will take care of
|
|
forwarding the call to the server-side object via JMS.</para>
|
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
|
|
|
<bean id="checkingAccountService"
|
|
class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
|
|
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
|
|
<property name="connectionFactory" ref="connectionFactory"/>
|
|
<property name="queue" ref="queue"/>
|
|
</bean>
|
|
|
|
</beans></programlisting>
|
|
|
|
<programlisting language="java">package com.foo;
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|
|
|
public class Client {
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
ApplicationContext ctx = new ClassPathXmlApplicationContext(
|
|
new String[] {"com/foo/client.xml", "com/foo/jms.xml"});
|
|
CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
|
|
service.cancelAccount(new Long(10));
|
|
}
|
|
}</programlisting>
|
|
</section>
|
|
|
|
<para>You may also wish to investigate the support provided by the <ulink
|
|
url="http://lingo.codehaus.org/">Lingo</ulink> project, which (to quote
|
|
the homepage blurb) <quote>
|
|
<emphasis>... is a lightweight POJO based remoting and messaging
|
|
library based on the Spring Framework's remoting libraries which
|
|
extends it to support JMS.</emphasis>
|
|
</quote></para>
|
|
</section>
|
|
|
|
<section id="remoting-autodection-remote-interfaces">
|
|
<title>Auto-detection is not implemented for remote interfaces</title>
|
|
|
|
<para>The main reason why auto-detection of implemented interfaces does
|
|
not occur for remote interfaces is to avoid opening too many doors to
|
|
remote callers. The target object might implement internal callback
|
|
interfaces like <interfacename>InitializingBean</interfacename> or
|
|
<interfacename>DisposableBean</interfacename> which one would not want to
|
|
expose to callers.</para>
|
|
|
|
<para>Offering a proxy with all interfaces implemented by the target
|
|
usually does not matter in the local case. But when exporting a remote
|
|
service, you should expose a specific service interface, with specific
|
|
operations intended for remote usage. Besides internal callback
|
|
interfaces, the target might implement multiple business interfaces, with
|
|
just one of them intended for remote exposure. For these reasons, we
|
|
<emphasis>require</emphasis> such a service interface to be
|
|
specified.</para>
|
|
|
|
<para>This is a trade-off between configuration convenience and the risk
|
|
of accidental exposure of internal methods. Always specifying a service
|
|
interface is not too much effort, and puts you on the safe side regarding
|
|
controlled exposure of specific methods.</para>
|
|
</section>
|
|
|
|
<section id="remoting-considerations">
|
|
<title>Considerations when choosing a technology</title>
|
|
|
|
<para>Each and every technology presented here has its drawbacks. You
|
|
should carefully consider you needs, the services your exposing and the
|
|
objects you'll be sending over the wire when choosing a technology.</para>
|
|
|
|
<para>When using RMI, it's not possible to access the objects through the
|
|
HTTP protocol, unless you're tunneling the RMI traffic. RMI is a fairly
|
|
heavy-weight protocol in that it support full-object serialization which
|
|
is important when using a complex data model that needs serialization over
|
|
the wire. However, RMI-JRMP is tied to Java clients: It is a Java-to-Java
|
|
remoting solution.</para>
|
|
|
|
<para>Spring's HTTP invoker is a good choice if you need HTTP-based
|
|
remoting but also rely on Java serialization. It shares the basic
|
|
infrastructure with RMI invokers, just using HTTP as transport. Note that
|
|
HTTP invokers are not only limited to Java-to-Java remoting but also to
|
|
Spring on both the client and server side. (The latter also applies to
|
|
Spring's RMI invoker for non-RMI interfaces.)</para>
|
|
|
|
<para>Hessian and/or Burlap might provide significant value when operating
|
|
in a heterogeneous environment, because they explicitly allow for non-Java
|
|
clients. However, non-Java support is still limited. Known issues include
|
|
the serialization of Hibernate objects in combination with
|
|
lazily-initialized collections. If you have such a data model, consider
|
|
using RMI or HTTP invokers instead of Hessian.</para>
|
|
|
|
<para>JMS can be useful for providing clusters of services and allowing
|
|
the JMS broker to take care of load balancing, discovery and
|
|
auto-failover. By default: Java serialization is used when using JMS
|
|
remoting but the JMS provider could use a different mechanism for the wire
|
|
formatting, such as XStream to allow servers to be implemented in other
|
|
technologies.</para>
|
|
|
|
<para>Last but not least, EJB has an advantage over RMI in that it
|
|
supports standard role-based authentication and authorization and remote
|
|
transaction propagation. It is possible to get RMI invokers or HTTP
|
|
invokers to support security context propagation as well, although this is
|
|
not provided by core Spring: There are just appropriate hooks for plugging
|
|
in third-party or custom solutions here.</para>
|
|
</section>
|
|
|
|
<section id="rest-client-access">
|
|
<title>Accessing RESTful services on the Client</title>
|
|
|
|
<para>The <classname>RestTemplate</classname> is the core class for
|
|
client-side access to RESTful services. It is conceptually similar to
|
|
other template classes in Spring, such as
|
|
<classname>JdbcTemplate</classname> and <classname>JmsTemplate</classname>
|
|
and other template classes found in other Spring portfolio projects.
|
|
<classname>RestTemplate</classname>'s behavior is customized by providing
|
|
callback methods and configuring the
|
|
<interfacename>HttpMessageConverter</interfacename> used to marshal
|
|
objects into the HTTP request body and to unmarshall any response back
|
|
into an object. As it is common to use XML as a message format, Spring
|
|
provides a <classname>MarshallingHttpMessageConverter</classname> that
|
|
uses the Object-to-XML framework that is part of the
|
|
<classname>org.springframework.oxm</classname> package. This gives you a
|
|
wide range of choices of XML to Object mapping technologies to choose
|
|
from.</para>
|
|
|
|
<para>This section describes how to use the
|
|
<classname>RestTemplate</classname> and its associated
|
|
<interfacename>HttpMessageConverters</interfacename>.</para>
|
|
|
|
<section id="rest-resttemplate">
|
|
<title>RestTemplate</title>
|
|
|
|
<para>Invoking RESTful services in Java is typically done using a helper
|
|
class such as Jakarta Commons <classname>HttpClient</classname>. For
|
|
common REST operations this approach is too low level as shown
|
|
below.</para>
|
|
|
|
<programlisting>String uri = "http://example.com/hotels/1/bookings";
|
|
|
|
PostMethod post = new PostMethod(uri);
|
|
String request = // create booking request content
|
|
post.setRequestEntity(new StringRequestEntity(request));
|
|
|
|
httpClient.executeMethod(post);
|
|
|
|
if (HttpStatus.SC_CREATED == post.getStatusCode()) {
|
|
Header location = post.getRequestHeader("Location");
|
|
if (location != null) {
|
|
System.out.println("Created new booking at :" + location.getValue());
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>RestTemplate provides higher level methods that correspond to each
|
|
of the six main HTTP methods that make invoking many RESTful services a
|
|
one-liner and enforce REST best practices.</para>
|
|
|
|
<table>
|
|
<title>Overview of RestTemplate methods</title>
|
|
|
|
<tgroup cols="2">
|
|
<tbody>
|
|
<row>
|
|
<entry><emphasis role="bold">HTTP Method</emphasis></entry>
|
|
|
|
<entry><emphasis role="bold">RestTemplate
|
|
Method</emphasis></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>DELETE</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#delete(String,%20String...)">delete(String
|
|
url, String… urlVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>GET</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject(String,%20Class,%20String...)">getForObject(String
|
|
url, Class<T> responseType, String…
|
|
urlVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>HEAD</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#headForHeaders(String,%20String...)">headForHeaders(String
|
|
url, String… urlVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>OPTIONS</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#optionsForAllow(String,%20String...)">optionsForAllow(String
|
|
url, String… urlVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>POST</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#postForLocation(String,%20Object,%20String...)">postForLocation(String
|
|
url, Object request, String… urlVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry></entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#postForObject(java.lang.String,%20java.lang.Object,%20java.lang.Class,%20java.lang.String...)">postForObject(String
|
|
url, Object request, Class<T> responseType, String…
|
|
uriVariables)</ulink></entry>
|
|
</row>
|
|
|
|
<row>
|
|
<entry>PUT</entry>
|
|
|
|
<entry><ulink
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#put(String,%20Object,%20String...)">put(String
|
|
url, Object request, String…urlVariables)</ulink></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
|
|
<para>The names of <classname>RestTemplate</classname> methods follow a
|
|
naming convention, the first part indicates what HTTP method is being
|
|
invoked and the second part indicates what is returned. For example, the
|
|
method <methodname>getForObject</methodname> will perform a GET, convert
|
|
the HTTP response into an object type of your choice and return that
|
|
object. The method <methodname>postForLocation</methodname> will do a
|
|
POST, converting the given object into a HTTP request and return the
|
|
response HTTP Location header where the newly created object can be
|
|
found. In case of an exception processing the HTTP request, an exception
|
|
of the type <classname>RestClientException</classname> will be
|
|
thrown, this behavior can be changed by plugging in another <interfacename>ResponseErrorHandler</interfacename>
|
|
implementation into the <classname>RestTemplate</classname>.</para>
|
|
|
|
<para>Objects passed to and returned from these methods are converted to
|
|
and from HTTP messages by
|
|
<interfacename>HttpMessageConverter</interfacename> instances.
|
|
Converters for the main mime types are registered by default, but you
|
|
can also write your own converter and register it via the
|
|
<methodname>messageConverters</methodname> bean property. The default
|
|
converter instances registered with the template are
|
|
<classname>ByteArrayHttpMessageConverter</classname>,
|
|
<classname>StringHttpMessageConverter</classname>,
|
|
<classname>FormHttpMessageConverter</classname> and
|
|
<classname>SourceHttpMessageConverter</classname>. You can override
|
|
these defaults using the <methodname>messageConverters</methodname> bean
|
|
property as would be required if using the
|
|
<classname>MarshallingHttpMessageConverter</classname>.</para>
|
|
|
|
<para>Each method takes URI template arguments in two forms, either as a
|
|
<literal>String</literal> variable length argument or a
|
|
<literal>Map<String,String></literal>. For example,</para>
|
|
|
|
<programlisting language="java">String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",
|
|
String.class,"42", "21");
|
|
</programlisting>
|
|
|
|
<para>using variable length arguments and</para>
|
|
|
|
<programlisting language="java">Map<String, String> vars = Collections.singletonMap("hotel", "42");
|
|
String result =
|
|
restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
|
|
</programlisting>
|
|
|
|
<para>using a <literal>Map<String,String></literal>.</para>
|
|
|
|
<para>To create an instance of <classname>RestTemplate</classname> you
|
|
can simply call the default constructor. This will use standard Java
|
|
classes from the <literal>java.net</literal> package as the underlying
|
|
implementation to create HTTP requests. This can be overridden by
|
|
specifying an implementation of
|
|
<interfacename>ClientHttpRequestFactory</interfacename>. Spring provides
|
|
the implementation
|
|
<classname>CommonsClientHttpRequestFactory</classname> that uses the
|
|
Jakarta Commons <classname>HttpClient</classname> to create requests.
|
|
<classname>CommonsClientHttpRequestFactory</classname> is configured
|
|
using an instance of
|
|
<classname>org.apache.commons.httpclient.HttpClient</classname> which
|
|
can in turn be configured with credentials information or connection
|
|
pooling functionality.</para>
|
|
|
|
<para>The previous example using Jakarta Commons
|
|
<classname>HttpClient</classname> directly rewritten to use the
|
|
<classname>RestTemplate</classname> is shown below</para>
|
|
|
|
<programlisting>uri = "http://example.com/hotels/{id}/bookings";
|
|
|
|
RestTemplate template = new RestTemplate();
|
|
|
|
Booking booking = // create booking object
|
|
|
|
URI location = template.postForLocation(uri, booking, "1");
|
|
</programlisting>
|
|
|
|
<para>The general callback interface is
|
|
<interfacename>RequestCallback</interfacename> and is called when the
|
|
execute method is invoked.</para>
|
|
|
|
<programlisting language="java">public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
|
|
ResponseExtractor<T> responseExtractor,
|
|
String... urlVariables)
|
|
|
|
|
|
// also has an overload with urlVariables as a Map<String, String>.</programlisting>
|
|
|
|
<para>The <interfacename>RequestCallback</interfacename> interface is
|
|
defined as</para>
|
|
|
|
<programlisting language="java">public interface RequestCallback {
|
|
void doWithRequest(ClientHttpRequest request) throws IOException;
|
|
}</programlisting>
|
|
|
|
<para>and allows you to manipulate the request headers and write to the
|
|
request body. When using the execute method you do not have to worry
|
|
about any resource management, the template will always close the
|
|
request and handle any errors. Refer to the API documentation for more
|
|
information on using the execute method and the meaning of its other
|
|
method arguments.</para>
|
|
</section>
|
|
|
|
<section id="rest-message-conversion">
|
|
<title>HTTP Message Conversion</title>
|
|
|
|
<para>Objects passed to and returned from the methods
|
|
<methodname>getForObject</methodname>,
|
|
<methodname>postForLocation</methodname>, and
|
|
<methodname>put</methodname> are converted to HTTP requests and from
|
|
HTTP responses by <interfacename>HttpMessageConverters</interfacename>.
|
|
The <interfacename>HttpMessageConverter</interfacename> interface is
|
|
shown below to give you a better feel for its functionality</para>
|
|
|
|
<programlisting language="java">public interface HttpMessageConverter<T> {
|
|
|
|
// Indicate whether the given class is supported by this converter.
|
|
boolean supports(Class<? extends T> clazz);
|
|
|
|
// Return the list of MediaType objects supported by this converter.
|
|
List<MediaType> getSupportedMediaTypes();
|
|
|
|
// Read an object of the given type form the given input message, and returns it.
|
|
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException,
|
|
HttpMessageNotReadableException;
|
|
|
|
// Write an given object to the given output message.
|
|
void write(T t, HttpOutputMessage outputMessage) throws IOException,
|
|
HttpMessageNotWritableException;
|
|
|
|
}</programlisting>
|
|
|
|
<para>Concrete implementations for the main media (mime) types are
|
|
provided in the framework and are registered by default with the
|
|
<classname>RestTemplate</classname> on the client-side and with
|
|
<classname>AnnotationMethodHandlerAdapter</classname> on the
|
|
server-side.</para>
|
|
|
|
<para>The implementations of
|
|
<classname>HttpMessageConverter</classname>s are described in the
|
|
following sections. For all converters a default media type is used but
|
|
can be overridden by setting the
|
|
<classname>supportedMediaTypes</classname> bean property</para>
|
|
|
|
<section id="rest-string-converter">
|
|
<title>StringHttpMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write Strings from the HTTP request
|
|
and response. By default, this converter supports all text media types
|
|
(<literal>text/*</literal>), and writes with a
|
|
<literal>Content-Type</literal> of
|
|
<literal>text/plain</literal>.</para>
|
|
</section>
|
|
|
|
<section id="rest-form-converter">
|
|
<title>FormHttpMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write form data from the HTTP request
|
|
and response. By default, this converter reads and writes the media
|
|
type <literal>application/x-www-form-urlencoded</literal>. Form data
|
|
is read from and written into a <literal>MultiValueMap<String,
|
|
String></literal>.</para>
|
|
</section>
|
|
|
|
<section id="rest-byte-converter">
|
|
<title>ByteArrayMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write byte arrays from the HTTP
|
|
request and response. By default, this converter supports all media
|
|
types (<literal>*/*</literal>), and writes with a
|
|
<literal>Content-Type</literal> of
|
|
<literal>application/octet-stream</literal>. This can be overridden by
|
|
setting the <property>supportedMediaTypes</property> property, and
|
|
overriding <literal>getContentType(byte[])</literal>.</para>
|
|
</section>
|
|
|
|
<section id="rest-marhsalling-converter">
|
|
<title>MarshallingHttpMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write XML using Spring's
|
|
<interfacename>Marshaller</interfacename> and
|
|
<interfacename>Unmarshaller</interfacename> abstractions from the
|
|
<classname>org.springframework.oxm</classname> package. This converter
|
|
requires a <interfacename>Marshaller</interfacename> and
|
|
<interfacename>Unmarshaller</interfacename> before it can be used.
|
|
These can be injected via constructor or bean properties. By default
|
|
this converter supports (<literal>text/xml</literal>) and
|
|
(<literal>application/xml</literal>).</para>
|
|
</section>
|
|
|
|
<section id="rest-source-converter">
|
|
<title>SourceHttpMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write
|
|
<classname>javax.xml.transform.Source</classname> from the HTTP
|
|
request and response. Only <classname>DOMSource</classname>,
|
|
<classname>SAXSource</classname>, and
|
|
<classname>StreamSource</classname> are supported. By default, this
|
|
converter supports (<literal>text/xml</literal>) and
|
|
(<literal>application/xml</literal>).</para>
|
|
</section>
|
|
|
|
<section id="rest-buffered-image-converter">
|
|
<title>BufferedImageHttpMessageConverter</title>
|
|
|
|
<para>An <interfacename>HttpMessageConverter</interfacename>
|
|
implementation that can read and write
|
|
<classname>java.awt.image.BufferedImage</classname> from the HTTP
|
|
request and response. This converter reads and writes the media type
|
|
supported by the Java I/O API.</para>
|
|
</section>
|
|
</section>
|
|
</section>
|
|
</chapter>
|