Resin 4 Hessian Examples

From Resin 4.0 Wiki

(Difference between revisions)
Jump to: navigation, search
 
Line 1: Line 1:
   
+
 
 +
{{Cookbook}} {{Development}}
 +
 
 +
=Hessian Tutorials=
 +
=Hessian Addition=
 +
      <p>The addition example creates a Hessian web services
 +
with a servlet and uses that web service from a
 +
JSP client and a Python client.</p>
 +
 
 +
<p>Hessian is a lightweight binary RPC protocol.  Transferring binary objects
 +
like files, images, or mp3s can avoid the protocol overhead
 +
that XML-based protocols require.  Since it's simple, its performance
 +
should be usable for almost all sites.  Hessian is designed to be
 +
self-describing, eliminating the requirement for external IDLs or WSDL files.
 +
Because it is as small as possible and language-independent,
 +
non-Java Hessian implementations are can easily develop comprehensive
 +
test suites.</p>
 +
 
 +
<p>This tutorial only requires the open source Java implementation of
 +
the Hessian client and server.  It can be downloaded
 +
from [http://caucho.com/hessian/ http://www.caucho.com/hessian/]
 +
for non-Resin clients and servers.</p>
 +
 
 +
<p>Because Resin's EJB implementation uses Hessian as its primary
 +
remote procedure call protocol, EJB developers can easily expose
 +
services to clients from other languages.</p>
 +
 
 +
<p>Because EJB clients and servers are written without knowledge of the
 +
underlying protocol, even if you intend to deploy with another protocol,
 +
like RMI/IIOP, you can develop using Resin's Hessian.</p>
 +
 
 +
<p>The [doc|hessian-1.0-spec.xtp Hessian 1.0 spec]
 +
and  [doc|hessian-2.0-spec.xtp Hessian 2.0 spec] describe
 +
the full Hessian protocol.</p>
 +
 
 +
==Files in this tutorial==
 +
<table>
 +
<tr>
 +
  <th>File</th>
 +
  <th>Description</th>
 +
</tr>
 +
<tr><td><code>WEB-INF/classes/example/MathService.java</code>
 +
    </td><td>Interface for the math service.
 +
</td></tr><tr><td><code>WEB-INF/classes/example/HessianMathService.java</code>
 +
    </td><td>The main service implementation.
 +
</td></tr><tr><td><code>WEB-INF/web.xml</code>
 +
    </td><td>Configures the environment
 +
</td></tr><tr><td><code>demo.jsp</code>
 +
    </td><td>Client JSP
 +
</td></tr></table>
 +
 
 +
==The Hessian Protocol==
 +
 
 +
<p>A Hessian call is just an HTTP POST to a URL.  The arguments are
 +
serialized into the Hessian binary format and passed to the server.</p>
 +
 
 +
<p>Most applications will never need to look at the Hessian
 +
protocol, but it's simple enough that a basic example can help show
 +
what's happening underneath the API.</p>
 +
 
 +
====Hessian call====
 +
<pre>
 +
c x01 x00
 +
  m x00 x03 add
 +
  I x00 x00 x00 x02
 +
  I x00 x00 x00 x03
 +
z
 +
</pre>
 +
 
 +
====Hessian reply====
 +
<pre>
 +
r x01 x00
 +
  I x00 x00 x00 x05
 +
z
 +
</pre>
 +
 
 +
<p>The call does not need to specify the service name because the
 +
service is uniquely specified by the URL.</p>
 +
 
 +
<p>The following Addition example shows how to create a basic
 +
server so you can test Hessian.</p>
 +
 
 +
 
 +
==A Hessian Example==
 +
<p>Using Hessian generally uses three components:</p>
 +
<ol>
 +
<li>A remote interface
 +
</li><li>The server implementation
 +
</li><li>The client (JSP or servlet)
 +
</li></ol>
 +
 
 +
<p>The remote interface is used by the Hessian proxy
 +
factory to create a proxy stub implementing the service's interface.</p>
 +
 
 +
 
 +
==Service Implementation==
 +
 
 +
<p>Resin's Hessian provides a simple way of creating a server.  Just extend
 +
<code>HessianServlet</code> with your remote methods.  The Hessian call will just
 +
be a POST to that servlet.  HessianServlet will introspect the
 +
service and expose the methods.</p>
 +
 
 +
====HessianMathService.java====
 +
<pre>
 +
package example;
 +
 
 +
import com.caucho.hessian.server.HessianServlet;
 +
 
 +
public class HessianMathService extends HessianServlet {
 +
  public int add(int a, int b)
 +
  {
 +
    return a + b;
 +
  }
 +
}
 +
</pre>
 +
 
 +
 
 +
==Remote Interface==
 +
 
 +
<p>The Java interface describes the remote API.  This example has an
 +
addition method, '''''add()'''''.</p>
 +
 
 +
<p>Resin's proxy client implementation uses the remote interface to
 +
expose the API to the proxy stub.  Strictly speaking, though,
 +
the Java remote interface is not required for Hessian.  A non-Java client
 +
will not use the Java interface, except possibly as documentation.</p>
 +
 
 +
====MathService.java====
 +
<pre>
 +
package example;
 +
 
 +
public interface MathService {
 +
  public int add(int a, int b);
 +
}
 +
</pre>
 +
 
 +
 
 +
==Java Client==
 +
 
 +
<p>RPC clients follow the following steps in using a remote object:</p>
 +
<ol>
 +
<li>Determine the URL of the remote object.
 +
</li><li>Obtain a proxy stub from a proxy factory.
 +
</li><li>Call methods on the proxy stub.
 +
</li></ol>
 +
 
 +
====client.jsp====
 +
<pre>
 +
<%@ page import="com.caucho.hessian.client.HessianProxyFactory" %>
 +
<%@ page import="example.MathService" %>
 +
<%
 +
HessianProxyFactory factory = new HessianProxyFactory();
 +
 
 +
// http://localhost:8080/resin-doc/protocols/tutorial/hessian-add/hessian/math
 +
 
 +
String url = ("http://" +
 +
              request.getServerName() + ":" + request.getServerPort() +
 +
              request.getContextPath() + "/hessian/math");
 +
 
 +
MathService math = (MathService) factory.create(MathService.class, url);
 +
 
 +
out.println("3 + 2 = " + math.add(3, 2));
 +
%>
 +
</pre>
 +
====results====
 +
<pre>
 +
3 + 2 = 5
 +
</pre>
 +
 
 +
 
 +
==Python Client==
 +
 
 +
<p>The [http://www.caucho.com/hessian Hessian site] has
 +
a basic Python library for Hessian.</p>
 +
 
 +
====client.py====
 +
<pre>
 +
from hessianlib import Hessian
 +
 
 +
site = "http://localhost:8080/resin-doc/protocols/tutorial/hessian-add"
 +
url = site + "/hessian/math"
 +
 
 +
proxy = Hessian(url);
 +
 
 +
print "3 + 2 =", proxy.add(2, 3)
 +
</pre>
 +
====results====
 +
<pre>
 +
3 + 2 = 5
 +
</pre>
 +
 
 +
 
 +
=Hessian with Dependency Injection=
 +
      <p>Using Hessian with Dependency Injection pattern
 +
creates services which are simpler, protocol-independent and more easily
 +
tested.  The Dependency Injection pattern (aka inversion of control)
 +
gives Resin the responsibility of configuring and assembling
 +
the service, protocol, and client.</p>
 +
 
 +
==Files in this tutorial==
 +
<table>
 +
<tr>
 +
  <th>File</th>
 +
  <th>Description</th>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/GreetingAPI.java</code></td>
 +
  <td>Interface for the greeting service.</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/GreetingImpl.java</code></td>
 +
  <td>The service implementation as a Java object</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/resin-web.xml</code></td>
 +
  <td>Configures the environment</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/GreetingClientServlet.java</code></td>
 +
  <td>Client Servlet</td>
 +
</tr>
 +
</table>
 +
 
 +
==Service Implementation==
 +
 
 +
====GreetingAPI.java====
 +
<pre>
 +
package example;
 +
 
 +
public interface GreetingAPI {
 +
  public String hello();
 +
}
 +
</pre>
 +
 
 +
===Service Implementation===
 +
 
 +
 
 +
<p>The Greeting implementation is a plain Java class that implements
 +
the MatchService API.  Making the service a plain class offers a number of advantages:</p>
 +
 
 +
<ul>
 +
<li><b>Simplicity:</b> It can concentrate on its business logic because it doesn't need to implement any protocol-dependent code.
 +
</li>
 +
<li><b>Independence:</b> It can be reused more easily because it isn't tied to a distributed framework (e.g. in contrast to EJB).
 +
</li>
 +
<li><b>Testability:</b> It is more easily tested since the test harness doesn't need to implement the protocol or its stubs.  The harness can just test the service as a plain old Java object.
 +
</li>
 +
</ul>
 +
 
 +
====GreetingImpl.java====
 +
<pre>
 +
package example;
 +
 
 +
public class GreetingImpl implements GreetingAPI {
 +
  private String _greeting = "Hello, world";
 +
 
 +
  public void setGreeting(String greeting)
 +
  {
 +
    _greeting = greeting;
 +
  }
 +
 
 +
  public String greeting()
 +
  {
 +
    return _greeting;
 +
  }
 +
}
 +
</pre>
 +
 
 +
 
 +
===Configuring the Service===
 +
 
 +
 
 +
<p>URL-based services can use the servlet configuration to define the
 +
service.  The service class can use
 +
[../../doc/resin-ioc.xtp Resin IoC] to inject its
 +
own dependencies.</p>
 +
 
 +
====resin-web.xml====
 +
<pre>
 +
<web-app xmlns="http://caucho.com/ns/resin"
 +
        xmlns:resin="urn:java:com.caucho.resin"
 +
        xmlns:example="urn:java:example">
 +
 
 +
  <example:GreetingImpl>
 +
    <resin:Unbound/>
 +
    <resin:HessianService urlPattern="/hessian/greeting"/>
 +
   
 +
    <greeting>Hello from resin-web.xml</greeting>
 +
  </example:GreetingImpl>
 +
 
 +
</web-app>
 +
</pre>
 +
 
 +
 
 +
 
 +
==Client==
 +
 
 +
<p>Configuring the client servlet with Dependency Injection
 +
allows for a simpler and more general client.  The following client
 +
can use any proxy/stub which implements the GreetingAPI without change,
 +
for example:</p>
 +
 
 +
<ul>
 +
  <li>Hessian proxy</li>
 +
  <li>Burlap proxy</li>
 +
  <li>EJB local object stub</li>
 +
  <li>JMX proxy</li>
 +
  <li>Java bean instance</li>
 +
</ul>
 +
 
 +
<p>Using the Dependency Injection pattern, the servlet doesn't care
 +
how the proxy is implemented, or how the greeting service is
 +
discovered.</p>
 +
 
 +
====GreetingClientServlet.java====
 +
<pre>
 +
import javax.inject.Inject;
 +
 
 +
public class GreetingClientServlet extends GenericServlet {
 +
  @Inject private GreetingAPI _greeting;
 +
 
 +
  public void service(ServletRequest req, ServletResponse res)
 +
    throws IOException, ServletException
 +
  {
 +
    PrintWriter out = res.getWriter();
 +
 
 +
    out.println("Hello: " + _greeting.greeting());
 +
  }
 +
}
 +
</pre>
 +
 
 +
===Hessian Client using Dependency Injection===
 +
 
 +
 
 +
<p>The following code defines the client in the resin.web.xml.
 +
The servlet defined above will inject the <code>GreetingAPI</code>
 +
directly with the WebBeans <code>@In</code> annotation.  Because the
 +
<code>GreetingAPI</code> is unique, there's no need to give it a name.</p>
 +
 
 +
====resin-web.xml====
 +
<pre>
 +
<web-app xmlns="http://caucho.com/ns/resin"
 +
        xmlns:resin="urn:java:com.caucho.resin"
 +
        xmlns:example="urn:java:example">
 +
 
 +
  <example:GreetingAPI>
 +
    <resin:HessianClient url="http:${webApp.url}/hessian/greeting"/>
 +
  </example:GreetingAPI>
 +
 
 +
  <servlet-mapping url-pattern="/client/greeting"
 +
                  servlet-class="example.GreetingClientServlet"/>
 +
 
 +
</web-app>
 +
</pre>
 +
 
 +
 
 +
=Hessian Service=
 +
      <p>Writing a Hessian service as a plain-old Java object (POJO)
 +
eliminates protocol dependencies and simplifies service testing.
 +
</p>
 +
 
 +
==Files in this tutorial==
 +
<table>
 +
<tr>
 +
  <th>File</th>
 +
  <th>Description</th>
 +
</tr>
 +
<tr><td><code>WEB-INF/classes/example/LogService.java</code>
 +
    </td><td>Interface for the logging service.
 +
</td></tr><tr><td><code>WEB-INF/classes/example/LogServiceImpl.java</code>
 +
    </td><td>The main service implementation.
 +
</td></tr><tr><td><code>WEB-INF/web.xml</code>
 +
    </td><td>Configures the environment
 +
</td></tr><tr><td><code>demo.jsp</code>
 +
    </td><td>Client JSP
 +
</td></tr></table>
 +
 
 +
==Service Implementation==
 +
 
 +
<p>The HelloService implementation is just a Java class that implements
 +
the HelloService API.  This service responds by placing the results of
 +
the request on a JMS queue.</p>
 +
 
 +
====LogService.java" language="java====
 +
<pre>
 +
package example;
 +
 
 +
public interface LogService {
 +
  public void log(String message);
 +
  public String getLog();
 +
}
 +
</pre>
 +
 
 +
====LogServiceImpl.java" language="java====
 +
<pre>
 +
package example;
 +
 
 +
public class LogServiceImpl implements LogService {
 +
  private int _sequenceNumber = 1;
 +
  private StringBuilder _log = new StringBuilder();
 +
 
 +
  public void log(String message)
 +
  {
 +
    _log.append(_sequenceNumber + ": " + message + "\n");
 +
    _sequenceNumber++;
 +
  }
 +
 
 +
  public String getLog()
 +
  {
 +
    return _log.toString();
 +
  }
 +
}
 +
</pre>
 +
 
 +
<p>
 +
The configuration for this example is the following:
 +
</p>
 +
 
 +
====web.xml" language="xml====
 +
<pre>
 +
<web-app xmlns="http://caucho.com/ns/resin">
 +
  <resource var="logService"
 +
            jndi-name="example/LogService"
 +
            type="example.LogServiceImpl" />
 +
 
 +
  <resource var="serviceQueue"
 +
            jndi-name="jms/ServiceQueue"
 +
            type="com.caucho.jms.memory.MemoryQueue" />
 +
 
 +
  <resource var="jmsFactory"
 +
            jndi-name="jms/ConnectionFactory"
 +
            type="com.caucho.jms.ConnectionFactoryImpl" />
 +
 
 +
  <resource type="com.caucho.hessian.server.HessianListener">
 +
    <init>
 +
      <connection-factory>${jmsFactory}</connection-factory>
 +
      <destination>${serviceQueue}</destination>
 +
      <service>${logService}</service>
 +
    </init>
 +
  </resource>
 +
</web-app>
 +
</pre>
 +
 
 +
<p>
 +
Finally, the service is called from a JSP page:
 +
</p>
 +
 
 +
====demo.jsp====
 +
<pre>
 +
<%@ page import="javax.naming.Context" %>
 +
<%@ page import="javax.naming.InitialContext" %>
 +
 
 +
<%@ page import="com.caucho.hessian.client.HessianProxyFactory" %>
 +
 
 +
<%@ page import="example.LogService" %>
 +
 
 +
<%
 +
// Check the log
 +
 
 +
Context context = (Context) new InitialContext().lookup("java:comp/env");
 +
LogService logService = (LogService) context.lookup("example/LogService");
 +
 
 +
out.println("<a href=\"\">Refresh</a><br/>");
 +
out.println("Logged messages:<br/>");
 +
out.println("<pre>");
 +
out.println(logService.getLog());
 +
out.println("&lt;/pre>");
 +
 
 +
// Make a request
 +
 
 +
HessianProxyFactory factory = new HessianProxyFactory();
 +
 
 +
String url = "jms:jms/ServiceQueue";
 +
 
 +
LogService log = (LogService) factory.create(LogService.class, url);
 +
 
 +
log.log("Hello, World");
 +
%>
 +
</pre>
 +
 
 +
<p>
 +
[demo.jsp Try the example]
 +
</p>
 +
 
 +
 
 +
=Hessian Serialization=
 +
      <p>Hessian 2.0 provides cross-language binary object serialization
 +
with efficiencies better than java.io serialization.  The compaction
 +
encodings added to Hessian 2.0 have improved an already-popular
 +
cross-platform binary web services protocol.  With these changes, Hessian
 +
2.0 now directly competes with java.io serialization in efficiency.</p>
 +
 
 +
==Files in this tutorial==
 +
<table>
 +
<tr>
 +
  <th>File</th>
 +
  <th>Description</th>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/HessianSerializeServlet.java</code></td>
 +
  <td>Serialization Servlet</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/Car.java</code></td>
 +
  <td>Serialized class</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/Color.java</code></td>
 +
  <td>Enumeration for the car color</td>
 +
</tr>
 +
<tr>
 +
  <td><code>WEB-INF/classes/example/Model.java</code></td>
 +
  <td>Enumeration for the car model</td>
 +
</tr>
 +
</table>
 +
 
 +
==Overview==
 +
 
 +
<p>In this simple example, we'll use Hessian 2.0 to serialize
 +
three Car objects to a byte array.  The serialized data could be saved in
 +
a persistent store, or sent as a message in a SOA or JMS application.
 +
Because Hessian 2.0 is cross-language, the message could be deserialized by a
 +
.NET or even a PHP application.</p>
 +
 
 +
<p>The efficiency of Hessian 2.0 is about twice that of java.io
 +
serialization. This is a tiny example, of course, but does show that you can
 +
send compact, cross-language messages without having to use bloated
 +
XML solutions like SOAP.</p>
 +
 
 +
<table>
 +
<tr>
 +
  <th>Service</th>
 +
  <th>Size</th>
 +
</tr>
 +
<tr>
 +
  <td>Hessian 2.0</td>
 +
  <td>139 bytes</td>
 +
</tr>
 +
<tr>
 +
  <td>java.io</td>
 +
  <td>287 bytes</td>
 +
</tr>
 +
<tr>
 +
  <td>Hessian 2.0 with Deflation</td>
 +
  <td>164 bytes</td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
==Model==
 +
 
 +
<p>The example's model is a Car object with three fields: year, model, and
 +
color.  The model and color are enumeration types.</p>
 +
 
 +
====Car.java====
 +
<pre>
 +
package example;
 +
 
 +
public class Car {
 +
  private int year;
 +
  private Model model;
 +
  private Color color;
 +
}
 +
</pre>
 +
 
 +
====Car.java====
 +
<pre>
 +
package example;
 +
 
 +
public enum Model {
 +
  CIVIC,
 +
  EDSEL,
 +
  MODEL_T,
 +
}
 +
</pre>
 +
 
 +
====Color.java====
 +
<pre>
 +
package example;
 +
 
 +
public enum Model {
 +
  BLACK,
 +
  GREEN,
 +
  BLUE,
 +
}
 +
</pre>
 +
 
 +
 
 +
==Hessian Serialization==
 +
 
 +
<p>The Hessian serialization API resembles
 +
java.io <code>ObjectOutputStream</code> serialization.  The general steps
 +
are to create a <code>Hessian2Output</code>
 +
around any <code>OutputStream</code> and write data to the stream.
 +
In this example, we've encapsulated the object in a Hessian 2.0 message
 +
using <code>startMessage</code> and <code>completeMessage</code> to
 +
show how you would create a message for an SOA or JMS application.</p>
 +
 
 +
====Serialization====
 +
<pre>
 +
ByteArrayOutputStream bos = new ByteArrayOutputStream();
 +
 
 +
HessianFactory factory = new HessianFactory();
 +
 
 +
Hessian2Output out = factory.createHessian2Output(bos);
 +
 
 +
out.startMessage();
 +
     
 +
out.writeInt(2);
 +
 
 +
Car car1 = new Car(Model.EDSEL, Color.GREEN, 1954);
 +
 
 +
out.writeObject(car1);
 +
 
 +
Car car2 = new Car(Model.MODEL_T, Color.BLACK, 1937);
 +
 
 +
out.writeObject(car2);
 +
 
 +
out.completeMessage();
 +
 
 +
out.close();
 +
 
 +
byte []data = bos.toByteArray();
 +
</pre>
 +
 
 +
<p>The deserialization is the same as serialization.
 +
Create an <code>Hessian2Input</code> around any <code>InputStream</code>
 +
and read data from the stream.</p>
 +
 
 +
====Deserialization====
 +
<pre>
 +
ByteArrayInputStream bin = new ByteArrayInputStream(data);
 +
 
 +
HessianFactory factory = new HessianFactory();
 +
 
 +
Hessian2Input in = factory.createHessianHessian2Input(bin);
 +
 
 +
in.startMessage();
 +
 
 +
ArrayList list = new ArrayList();
 +
 
 +
int length = in.readInt();
 +
 
 +
for (int i = 0; i < length; i++) {
 +
  list.add(in.readObject());
 +
}
 +
 
 +
in.completeMessage();
 +
 
 +
in.close();
 +
bin.close();
 +
</pre>
 +
 
 +
 
 +
==Hessian Compression==
 +
 
 +
<p>The <a href="http://caucho.com/resin-3.1/doc/hessian-2.0-spec.xtp">Hessian
 +
2.0 draft specification</a> has added support for envelopes around Hessian
 +
messages.  These envelopes can provide additional capabilities like
 +
compression, encryption, and message signatures.  The envelope can also
 +
be used to attach routing and reliability information to a message.  Since
 +
envelopes are nestable, each envelope can be simple and provide powerful
 +
capabilities when combined.  For example, a secure messaging system
 +
might compress, encrypt and then securely sign a message.</p>
 +
 
 +
<p>The API for using envelopes is <code>wrap()</code> for writing a message
 +
and <code>unwrap()</code> for reading a message.  The application
 +
serialization code itself is identical, since the envelope just creates a
 +
<code>Hessian2Input</code> or <code>Hessian2Output</code> wrapper around
 +
the original stream.</p>
 +
 
 +
====Deflation====
 +
<pre>
 +
Deflation envelope = new Deflation();
 +
 
 +
ByteArrayOutputStream bos = new ByteArrayOutputStream();
 +
 
 +
HessianFactory factory = new HessianFactory();
 +
Hessian2Output out = facotyr.createHessian2Output(bos);
 +
 
 +
out = out.wrap(out);
 +
 
 +
out.startMessage();
 +
 
 +
Car car1 = new Car(Model.EDSEL, Color.GREEN, 1954);
 +
 
 +
out.writeObject(car1);
 +
 
 +
out.completeMessage();
 +
 
 +
out.close();
 +
 
 +
byte []data = bos.toByteArray();
 +
</pre>
 +
 
 +
====Inflation====
 +
<pre>
 +
Deflation envelope = new Deflation();
 +
 
 +
ByteArrayInputStream bin = new ByteArrayInputStream(data);
 +
 
 +
HessianFactory factory = new HessianFactory();
 +
Hessian2Input in = factory.createHessian2Input(bin);
 +
 
 +
in = envelope.unwrap(in);
 +
 
 +
in.startMessage();
 +
 
 +
Object value = in.readObject();
 +
 
 +
in.completeMessage();
 +
</pre>
 +
 
 +
 
 +
=Hessian Service=
 
       <p>Writing a Hessian service as a plain-old Java object (POJO)
 
       <p>Writing a Hessian service as a plain-old Java object (POJO)
 
eliminates protocol dependencies and simplifies service testing.
 
eliminates protocol dependencies and simplifies service testing.
Line 8: Line 722:
 
will want to be independent of the Hessian protocol itself.</p>
 
will want to be independent of the Hessian protocol itself.</p>
  
=Files in this tutorial=
+
==Files in this tutorial==
 
<table>
 
<table>
 
<tr>
 
<tr>
Line 36: Line 750:
 
</table>
 
</table>
  
=Service Implementation=
+
==Service Implementation==
  
 
<p>The MathService implementation is just a Java class that implements
 
<p>The MathService implementation is just a Java class that implements
Line 54: Line 768:
  
  
=Remote Interface=
+
==Remote Interface==
  
 
<p>The Java interface describes the remote API.  This example has an
 
<p>The Java interface describes the remote API.  This example has an
Line 74: Line 788:
  
  
=Service configuration=
+
==Service configuration==
  
 
====Example: resin-web.xml====
 
====Example: resin-web.xml====
Line 94: Line 808:
  
  
=Java Client=
+
==Java Client==
  
 
<p>The client is identical to the basic example.</p>
 
<p>The client is identical to the basic example.</p>
Line 110: Line 824:
 
3 * 2 = <%= math.mul(3, 2) %>
 
3 * 2 = <%= math.mul(3, 2) %>
 
3 / 2 = <%= math.div(3, 2) %>
 
3 / 2 = <%= math.div(3, 2) %>
 +
&lt;/pre>
 
</pre>
 
</pre>
</pre>
+
====results====
 
<pre>
 
<pre>
 
3 + 2 = 5
 
3 + 2 = 5
Line 120: Line 835:
  
  
=PHP Client=
+
==PHP Client==
  
 
<p>The client is identical to the basic example.</p>
 
<p>The client is identical to the basic example.</p>
Line 135: Line 850:
 
3 * 2 = <?= $math->mul(3, 2) ?>
 
3 * 2 = <?= $math->mul(3, 2) ?>
 
3 / 2 = <?= $math->div(3, 2) ?>
 
3 / 2 = <?= $math->div(3, 2) ?>
 +
&lt;/pre>
 
</pre>
 
</pre>
</pre>
+
====results====
 
<pre>
 
<pre>
 
3 + 2 = 5
 
3 + 2 = 5

Latest revision as of 00:00, 16 June 2012

Cookbook-48.png

Construction-48.png

Contents

Hessian Tutorials

Hessian Addition

The addition example creates a Hessian web services with a servlet and uses that web service from a JSP client and a Python client.

Hessian is a lightweight binary RPC protocol. Transferring binary objects like files, images, or mp3s can avoid the protocol overhead that XML-based protocols require. Since it's simple, its performance should be usable for almost all sites. Hessian is designed to be self-describing, eliminating the requirement for external IDLs or WSDL files. Because it is as small as possible and language-independent, non-Java Hessian implementations are can easily develop comprehensive test suites.

This tutorial only requires the open source Java implementation of the Hessian client and server. It can be downloaded from http://www.caucho.com/hessian/ for non-Resin clients and servers.

Because Resin's EJB implementation uses Hessian as its primary remote procedure call protocol, EJB developers can easily expose services to clients from other languages.

Because EJB clients and servers are written without knowledge of the underlying protocol, even if you intend to deploy with another protocol, like RMI/IIOP, you can develop using Resin's Hessian.

The [doc|hessian-1.0-spec.xtp Hessian 1.0 spec] and [doc|hessian-2.0-spec.xtp Hessian 2.0 spec] describe the full Hessian protocol.

Files in this tutorial

File Description
WEB-INF/classes/example/MathService.java Interface for the math service.
WEB-INF/classes/example/HessianMathService.java The main service implementation.
WEB-INF/web.xml Configures the environment
demo.jsp Client JSP

The Hessian Protocol

A Hessian call is just an HTTP POST to a URL. The arguments are serialized into the Hessian binary format and passed to the server.

Most applications will never need to look at the Hessian protocol, but it's simple enough that a basic example can help show what's happening underneath the API.

Hessian call

c x01 x00
  m x00 x03 add
  I x00 x00 x00 x02
  I x00 x00 x00 x03
z

Hessian reply

r x01 x00
  I x00 x00 x00 x05
z

The call does not need to specify the service name because the service is uniquely specified by the URL.

The following Addition example shows how to create a basic server so you can test Hessian.


A Hessian Example

Using Hessian generally uses three components:

  1. A remote interface
  2. The server implementation
  3. The client (JSP or servlet)

The remote interface is used by the Hessian proxy factory to create a proxy stub implementing the service's interface.


Service Implementation

Resin's Hessian provides a simple way of creating a server. Just extend HessianServlet with your remote methods. The Hessian call will just be a POST to that servlet. HessianServlet will introspect the service and expose the methods.

HessianMathService.java

package example;

import com.caucho.hessian.server.HessianServlet;

public class HessianMathService extends HessianServlet {
  public int add(int a, int b)
  {
    return a + b;
  }
}


Remote Interface

The Java interface describes the remote API. This example has an addition method, add().

Resin's proxy client implementation uses the remote interface to expose the API to the proxy stub. Strictly speaking, though, the Java remote interface is not required for Hessian. A non-Java client will not use the Java interface, except possibly as documentation.

MathService.java

package example;

public interface MathService {
  public int add(int a, int b);
}


Java Client

RPC clients follow the following steps in using a remote object:

  1. Determine the URL of the remote object.
  2. Obtain a proxy stub from a proxy factory.
  3. Call methods on the proxy stub.

client.jsp

<%@ page import="com.caucho.hessian.client.HessianProxyFactory" %>
<%@ page import="example.MathService" %>
<%
HessianProxyFactory factory = new HessianProxyFactory();

// http://localhost:8080/resin-doc/protocols/tutorial/hessian-add/hessian/math

String url = ("http://" +
              request.getServerName() + ":" + request.getServerPort() +
              request.getContextPath() + "/hessian/math");

MathService math = (MathService) factory.create(MathService.class, url);

out.println("3 + 2 = " + math.add(3, 2));
%>

results

3 + 2 = 5


Python Client

The Hessian site has a basic Python library for Hessian.

client.py

from hessianlib import Hessian

site = "http://localhost:8080/resin-doc/protocols/tutorial/hessian-add"
url = site + "/hessian/math"

proxy = Hessian(url);

print "3 + 2 =", proxy.add(2, 3)

results

3 + 2 = 5


Hessian with Dependency Injection

Using Hessian with Dependency Injection pattern creates services which are simpler, protocol-independent and more easily tested. The Dependency Injection pattern (aka inversion of control) gives Resin the responsibility of configuring and assembling the service, protocol, and client.

Files in this tutorial

File Description
WEB-INF/classes/example/GreetingAPI.java Interface for the greeting service.
WEB-INF/classes/example/GreetingImpl.java The service implementation as a Java object
WEB-INF/resin-web.xml Configures the environment
WEB-INF/classes/example/GreetingClientServlet.java Client Servlet

Service Implementation

GreetingAPI.java

package example;

public interface GreetingAPI {
  public String hello();
}

Service Implementation

The Greeting implementation is a plain Java class that implements the MatchService API. Making the service a plain class offers a number of advantages:

  • Simplicity: It can concentrate on its business logic because it doesn't need to implement any protocol-dependent code.
  • Independence: It can be reused more easily because it isn't tied to a distributed framework (e.g. in contrast to EJB).
  • Testability: It is more easily tested since the test harness doesn't need to implement the protocol or its stubs. The harness can just test the service as a plain old Java object.

GreetingImpl.java

package example;

public class GreetingImpl implements GreetingAPI {
  private String _greeting = "Hello, world";

  public void setGreeting(String greeting)
  {
    _greeting = greeting;
  }

  public String greeting()
  {
    return _greeting;
  }
}


Configuring the Service

URL-based services can use the servlet configuration to define the service. The service class can use [../../doc/resin-ioc.xtp Resin IoC] to inject its own dependencies.

resin-web.xml

<web-app xmlns="http://caucho.com/ns/resin"
         xmlns:resin="urn:java:com.caucho.resin"
         xmlns:example="urn:java:example">

  <example:GreetingImpl>
    <resin:Unbound/>
    <resin:HessianService urlPattern="/hessian/greeting"/>
    
    <greeting>Hello from resin-web.xml</greeting>
  </example:GreetingImpl>

</web-app>


Client

Configuring the client servlet with Dependency Injection allows for a simpler and more general client. The following client can use any proxy/stub which implements the GreetingAPI without change, for example:

  • Hessian proxy
  • Burlap proxy
  • EJB local object stub
  • JMX proxy
  • Java bean instance

Using the Dependency Injection pattern, the servlet doesn't care how the proxy is implemented, or how the greeting service is discovered.

GreetingClientServlet.java

import javax.inject.Inject;

public class GreetingClientServlet extends GenericServlet {
  @Inject private GreetingAPI _greeting;

  public void service(ServletRequest req, ServletResponse res)
    throws IOException, ServletException
  {
    PrintWriter out = res.getWriter();

    out.println("Hello: " + _greeting.greeting());
  }
}

Hessian Client using Dependency Injection

The following code defines the client in the resin.web.xml. The servlet defined above will inject the GreetingAPI directly with the WebBeans @In annotation. Because the GreetingAPI is unique, there's no need to give it a name.

resin-web.xml

<web-app xmlns="http://caucho.com/ns/resin"
        xmlns:resin="urn:java:com.caucho.resin"
        xmlns:example="urn:java:example">

  <example:GreetingAPI>
    <resin:HessianClient url="http:${webApp.url}/hessian/greeting"/>
  </example:GreetingAPI>

  <servlet-mapping url-pattern="/client/greeting"
                   servlet-class="example.GreetingClientServlet"/>

</web-app>


Hessian Service

Writing a Hessian service as a plain-old Java object (POJO) eliminates protocol dependencies and simplifies service testing.

Files in this tutorial

File Description
WEB-INF/classes/example/LogService.java Interface for the logging service.
WEB-INF/classes/example/LogServiceImpl.java The main service implementation.
WEB-INF/web.xml Configures the environment
demo.jsp Client JSP

Service Implementation

The HelloService implementation is just a Java class that implements the HelloService API. This service responds by placing the results of the request on a JMS queue.

LogService.java" language="java

package example;

public interface LogService {
  public void log(String message);
  public String getLog();
}

LogServiceImpl.java" language="java

package example;

public class LogServiceImpl implements LogService {
  private int _sequenceNumber = 1;
  private StringBuilder _log = new StringBuilder();

  public void log(String message)
  {
    _log.append(_sequenceNumber + ": " + message + "\n");
    _sequenceNumber++;
  }

  public String getLog()
  {
    return _log.toString();
  }
}

The configuration for this example is the following:

web.xml" language="xml

<web-app xmlns="http://caucho.com/ns/resin">
  <resource var="logService"
            jndi-name="example/LogService"
            type="example.LogServiceImpl" />

  <resource var="serviceQueue" 
            jndi-name="jms/ServiceQueue"
            type="com.caucho.jms.memory.MemoryQueue" />
  
  <resource var="jmsFactory"
            jndi-name="jms/ConnectionFactory"
            type="com.caucho.jms.ConnectionFactoryImpl" />

  <resource type="com.caucho.hessian.server.HessianListener">
    <init>
      <connection-factory>${jmsFactory}</connection-factory>
      <destination>${serviceQueue}</destination>
      <service>${logService}</service>
    </init>
  </resource>
</web-app>

Finally, the service is called from a JSP page:

demo.jsp

<%@ page import="javax.naming.Context" %>
<%@ page import="javax.naming.InitialContext" %>

<%@ page import="com.caucho.hessian.client.HessianProxyFactory" %>

<%@ page import="example.LogService" %>

<%
// Check the log

Context context = (Context) new InitialContext().lookup("java:comp/env");
LogService logService = (LogService) context.lookup("example/LogService");

out.println("<a href=\"\">Refresh</a><br/>");
out.println("Logged messages:<br/>");
out.println("<pre>");
out.println(logService.getLog());
out.println("</pre>");

// Make a request

HessianProxyFactory factory = new HessianProxyFactory();

String url = "jms:jms/ServiceQueue";

LogService log = (LogService) factory.create(LogService.class, url);

log.log("Hello, World");
%>

[demo.jsp Try the example]


Hessian Serialization

Hessian 2.0 provides cross-language binary object serialization with efficiencies better than java.io serialization. The compaction encodings added to Hessian 2.0 have improved an already-popular cross-platform binary web services protocol. With these changes, Hessian 2.0 now directly competes with java.io serialization in efficiency.

Files in this tutorial

File Description
WEB-INF/classes/example/HessianSerializeServlet.java Serialization Servlet
WEB-INF/classes/example/Car.java Serialized class
WEB-INF/classes/example/Color.java Enumeration for the car color
WEB-INF/classes/example/Model.java Enumeration for the car model

Overview

In this simple example, we'll use Hessian 2.0 to serialize three Car objects to a byte array. The serialized data could be saved in a persistent store, or sent as a message in a SOA or JMS application. Because Hessian 2.0 is cross-language, the message could be deserialized by a .NET or even a PHP application.

The efficiency of Hessian 2.0 is about twice that of java.io serialization. This is a tiny example, of course, but does show that you can send compact, cross-language messages without having to use bloated XML solutions like SOAP.

Service Size
Hessian 2.0 139 bytes
java.io 287 bytes
Hessian 2.0 with Deflation 164 bytes


Model

The example's model is a Car object with three fields: year, model, and color. The model and color are enumeration types.

Car.java

package example;

public class Car {
  private int year;
  private Model model;
  private Color color;
}

Car.java

package example;

public enum Model {
  CIVIC,
  EDSEL,
  MODEL_T,
}

Color.java

package example;

public enum Model {
  BLACK,
  GREEN,
  BLUE,
}


Hessian Serialization

The Hessian serialization API resembles java.io ObjectOutputStream serialization. The general steps are to create a Hessian2Output around any OutputStream and write data to the stream. In this example, we've encapsulated the object in a Hessian 2.0 message using startMessage and completeMessage to show how you would create a message for an SOA or JMS application.

Serialization

ByteArrayOutputStream bos = new ByteArrayOutputStream();

HessianFactory factory = new HessianFactory();

Hessian2Output out = factory.createHessian2Output(bos);

out.startMessage();
      
out.writeInt(2);

Car car1 = new Car(Model.EDSEL, Color.GREEN, 1954);

out.writeObject(car1);

Car car2 = new Car(Model.MODEL_T, Color.BLACK, 1937);

out.writeObject(car2);

out.completeMessage();

out.close();

byte []data = bos.toByteArray();

The deserialization is the same as serialization. Create an Hessian2Input around any InputStream and read data from the stream.

Deserialization

ByteArrayInputStream bin = new ByteArrayInputStream(data);

HessianFactory factory = new HessianFactory();

Hessian2Input in = factory.createHessianHessian2Input(bin);

in.startMessage();

ArrayList list = new ArrayList();

int length = in.readInt();

for (int i = 0; i < length; i++) {
  list.add(in.readObject());
}

in.completeMessage();

in.close();
bin.close();


Hessian Compression

The <a href="http://caucho.com/resin-3.1/doc/hessian-2.0-spec.xtp">Hessian 2.0 draft specification</a> has added support for envelopes around Hessian messages. These envelopes can provide additional capabilities like compression, encryption, and message signatures. The envelope can also be used to attach routing and reliability information to a message. Since envelopes are nestable, each envelope can be simple and provide powerful capabilities when combined. For example, a secure messaging system might compress, encrypt and then securely sign a message.

The API for using envelopes is wrap() for writing a message and unwrap() for reading a message. The application serialization code itself is identical, since the envelope just creates a Hessian2Input or Hessian2Output wrapper around the original stream.

Deflation

Deflation envelope = new Deflation();

ByteArrayOutputStream bos = new ByteArrayOutputStream();

HessianFactory factory = new HessianFactory();
Hessian2Output out = facotyr.createHessian2Output(bos);

out = out.wrap(out);

out.startMessage();

Car car1 = new Car(Model.EDSEL, Color.GREEN, 1954);

out.writeObject(car1);

out.completeMessage();

out.close();

byte []data = bos.toByteArray();

Inflation

Deflation envelope = new Deflation();

ByteArrayInputStream bin = new ByteArrayInputStream(data);

HessianFactory factory = new HessianFactory();
Hessian2Input in = factory.createHessian2Input(bin);

in = envelope.unwrap(in);

in.startMessage();

Object value = in.readObject();

in.completeMessage();


Hessian Service

Writing a Hessian service as a plain-old Java object (POJO) eliminates protocol dependencies and simplifies service testing.

The [../hessian-add/ addition example] built the Hessian service as an extension of HessianService for simplicity. Most services will want to be independent of the Hessian protocol itself.

Files in this tutorial

File Description
WEB-INF/classes/example/MathService.java Interface for the math service.
WEB-INF/classes/example/MathServiceImpl.java The main service implementation.
WEB-INF/resin-web.xml Configures the environment
demo.jsp Client JSP
demo.php Client PHP

Service Implementation

The MathService implementation is just a Java class that implements the MatchService API.

Example: MathServiceImpl.java

package example;

public class MathServiceImpl implements MathService {
  public int add(int a, int b)
  {
    return a + b;
  }
}


Remote Interface

The Java interface describes the remote API. This example has an addition method, add().

Resin's proxy client implementation uses the remote interface to expose the API to the proxy stub. Strictly speaking, though, the Java remote interface is not required for Hessian. A non-Java client will not use the Java interface, except possibly as documentation.

Example: MathService.java

package example;

public interface MathService {
  public int add(int a, int b);
}


Service configuration

Example: resin-web.xml

<web-app xmlns="http://caucho.com/ns/resin">

  <servlet-mapping url-pattern="/math/*"
                   servlet-class="example.MathService">
    <protocol uri="hessian:"/>
  </servlet-mapping>

  <remote-client name="math">
    <uri>hessian:url=${webApp.url}/math/</uri>
    <interface>example.MathService</interface>
  </remote-client>

</web-app>


Java Client

The client is identical to the basic example.

Example: demo.jsp

<%@ page import="javax.webbeans.In" %>
<%@ page import="example.MathService" %>
<%!
@In MathService math;
%>
<pre>
3 + 2 = <%= math.add(3, 2) %>
3 - 2 = <%= math.sub(3, 2) %>
3 * 2 = <%= math.mul(3, 2) %>
3 / 2 = <%= math.div(3, 2) %>
</pre>

results

3 + 2 = 5
3 - 2 = 1
3 * 2 = 6
3 / 2 = 1


PHP Client

The client is identical to the basic example.

Example: demo.php

<?php

$math = java_bean("math");
?>
<pre>
3 + 2 = <?= $math->add(3, 2) ?>
3 - 2 = <?= $math->sub(3, 2) ?>
3 * 2 = <?= $math->mul(3, 2) ?>
3 / 2 = <?= $math->div(3, 2) ?>
</pre>

results

3 + 2 = 5
3 - 2 = 1
3 * 2 = 6
3 / 2 = 1
Personal tools
TOOLBOX
LANGUAGES