![]() | ||||||||||||||||||||||||||||||||||
| documentation examples changes overview quick start installation command-line configuration guide: admin admin amber clustering caching database deployment ejb 3.0 embedding filters hessian hmtp ioc jsf jsp logging messaging performance quercus/php remoting scheduled tasks security server push servlets third-party troubleshooting virtual hosting watchdog webapp xml and xslt | hmtp
HMTP (Hessian Message Transport Protocol) is an asynchronous, object-oriented messaging framework, designed around small, interactive message packets, a brokered-agent topology and based on the Hessian protocol and XMPP (Jabber). Server message exampleSending a message from one agent to another is a basic HMTP use case. Typical applications include chat text, game updates, Atom/RSS updates, pub/sub messaging, and event notification for administration consoles. In this example, a servlet send a message to an internal logging service. The sending agent sends a message using HMTP messages use a JID (Jabber ID) for addressing, which looks like
The message payload can be any serializable object. Because Hessian is available for many languages, the message can easily interoperate with RIA platforms like Flash. Since HMTP uses a broker/agent or hub-and-spoke messaging model, all messages go through the broker to be routed to the target agent. Each service and client will register an agent with the broker and receive a unique JID for the routing. ![]() Writing a HMTP client involves the following steps:
In the example, the servlet creates a TestServlet.java
package example;
import javax.servlet.*;
import javax.webbeans.In;
import com.caucho.bam.BamConnectionFactory;
import com.caucho.bam.BamConnection;
public class TestServlet extends GenericServlet
{
@In BamConnectionFactory _factory;
public void service(ServletRequest req, ServletResponse response)
{
BamConnection conn = _factory.getConnection("demo@localhost", null);
try {
conn.message("test@localhost", "Hello, world!");
} finally {
conn.close();
}
}
}
Writing a HMTP Service involves the following steps:
By extending LogService.java
package example;
import com.caucho.hemp.broker.GenericService;
import java.io.Serializable;
import java.util.logging.*;
public class LogService extends GenericService
{
private static final Logger log
= Logger.getLogger(LogService.class.getName());
@Override
public void message(String to, String from, Serializable value)
{
log.info(this + " message from=" + from + " value=" + value);
}
}
The HMTP configuration consists of the broker and the service
configured with Resin IoC. Because
the WEB-INF/resin-web.xml
<web-app xmlns="http://caucho.com/ns/resin">
<bean name="test@localhost" class="example.LogService"/>
</web-app>
Client queryGet (RPC) exampleRemote calls are another primary use for HMTP. In this example, we just query a service for some basic information. In HMTP, queries are bidirectional: the server can also query the client. And the application can also use the messaging in the previous example. ![]() To implement the server side of an RPC call, the service implements
The type-based query system gives enormous flexibility in creating services. Services can be mash-ups of capabilities just by adding new query types. TestService.java
package example;
import com.caucho.hemp.broker.GenericService;
import java.io.Serializable;
import java.util.logging.*;
public class TestService extends GenericService
{
private static final Logger log
= Logger.getLogger(LogService.class.getName());
@Override
public boolean sendQueryGet(long id, String to, String from,
Serializable query)
{
if (query instanceof TestQuery) {
getBrokerStream().sendQueryResult(id, to, from, "hello response");
return true;
}
else {
return super.sendQueryGet(id, to, from, query);
}
}
}
The remote client, When you create a This example sends a RPC query to .
RPC calls in HMTP are typed. Each query class might execute a different
query on the server. In this case we create a trivial TestClient.java
package example;
import com.caucho.hmtp.client.HmtpClient;
public class TestClient
{
public static void main(String []args)
throws Exception
{
HmtpClient client = new HmtpClient("http://localhost:8080/hmtp");
client.connect();
client.login("user@localhost", null);
Object value = client.queryGet("test@localhost", new TestQuery());
System.out.println(value);
client.close();
}
}
The configuration for a remote service now has three components:
WEB-INF/resin-web.xml
<web-app xmlns="http://caucho.com/ns/resin">
<bean name="test@localhost" class="example.TestService"/>
<servlet-mapping url-pattern="/hmtp"
servlet-class="com.caucho.hemp.servlet.HempServlet"/>
</web-app>
Applications using HMTP will generally follow a Brokered Agent Messaging pattern, which is basically a hub-and-spoke messaging topology where the agents act as dynamic services: joining and detaching from the broker as the application progresses. Services and clients register one or more agents with the BamBroker and then send messages between the agents. Each remote client will register a local agent with the local HMTP broker. Services will register one or more agents with the broker. In a tic-tac-toe game, the game instance might register two agents: one for each player in a particular game. ![]() The diagram above has four agents: two agents for the game's players, and one agent for each logged-in user. is the game's agent for player #1, and is Harry's agent for his flash client. In the tic-tac-toe game, each user's agent talks to the matching game player, so always talks to , and always talks to . The game's agents are ephemeral. When a new game begins, a
Because the game's agents are only created when a game begins, the tic-tac-toe game has a persistent agent for registration, . When Harry logs on, the client will send a query to asking for a new game. As soon as Draco asks for a match, the registration server will create a new game instance and tell Harry's client the name of his player agent, . ![]() HMTP is an adaptation of the XMPP (Jabber) instant messaging protocol. Where XMPP (Xml Messaging and Presence Protocol) is based on XML, HMTP (Hessian Message Transport Protocol) is based on Hessian. Because HMTP is designed to follow XMPP, its architecture and protocols are essentially identical until the very lowest layer, where HMTP substitutes Hessian for XML. Because of the close relationship to XMPP, you may want to browse the XMPP specifications for a deeper understanding of how HMTP works. Since XMPP is only a wire protocol, not an API, it does not include all of the HMTP classes, but the architecture remains the same. The primary advantages HMTP offers over XMPP include the performance
advantages of Hessian over XML, and more importantly a more strict layering
than XMPP provides. Because the payloads of the HMTP messages are all
HMTP provides three categories of packets: messages, queries (rpc), and presence announcements. Messages and queries are typically the bulk of the packets, while presence announcements are used rarely. Messages are unidirectional fire-and-forget packets. Queries are request-response pairs. Each request must have a corresponding response or error. Presence announcements are used to organize subscriptions. There are presence announcements to subscribe and unsubscribe, and presence notifications that a user has logged on, sent to all other users subscribed to his presence. Message PacketsThe main Message packet contains a target ("to"), a sender ("from"), and a payload ("value"). In HMTP, the payload can be any serializable value. Example messages could be IM text messages, administration console graph, game updates, or updated stock quotes. Since HMTP is bidirectional, messages can flow to and from any client.
Query PacketsQuery packages are RPC get and set packets with a matching response or error. Because the query will always have a matching response packet or an error packet, clients can either block for the result or attach a callback. Like the other packets, queries are bidirectional, so a service can query a client as well as the usual client querying the server. Query packets have an associated field to match requests with responses. The client will increment the for each new query.
Presence PacketsPresence packets send specialized information for subscription notification. Many applications will not need to use any presence packets at all.
HMTP resources all have unique identifiers called JIDs (Jabber IDs). The id looks like: JID format @/ The and are optional.
Client APIBamConnection
An active For clients that need low-level access to the broker stream, e.g. to
implement an RPC/Query handler, BamConnection
package com.caucho.bam;
public interface BamConnection
{
String getJid();
boolean isClosed();
void close();
void setStreamHandler(BamStream handler);
void message(String to, Serializable value);
Serializable queryGet(String to, Serializable query);
Serializable querySet(String to, Serializable query);
void queryGet(String to, Serializable query, BamQueryCallback callback);
void querySet(String to, Serializable query, BamQueryCallback callback);
void presence(Serializable data);
void presence(String to, Serializable data);
void presenceUnavailable(Serializable data);
void presenceUnavailable(String to, Serializable data);
void presenceProbe(String to, Serializable data);
void presenceSubscribe(String to, Serializable data);
void presenceSubscribed(String to, Serializable data);
void presenceUnsubscribe(String to, Serializable data);
void presenceUnsubscribed(String to, Serializable data);
void presenceError(String to, Serializable data, BamError error);
BamStream getBrokerStream();
}
BamConnectionFactoryThe BamConnectionFactory
package com.caucho.bam;
public interface BamConnectionFactory
{
BamConnection getConnection(String uid, String password);
BamConnection getConnection(String uid, String password, String resourceId);
}
BamQueryCallback
QueryCallback
package com.caucho.bam;
public interface BamQueryCallback
{
void onQueryResult(String to, String from, Serializable value);
void onQueryError(String to, String from, Serializable value,
BamError error);
}
Remote Client API
HmtpClientHmtpClient
package com.caucho.bam;
public class HmtpClient implements BamConnection
{
public HmtpClient(String url);
public void connect() throws IOException;
public void login(String uid, String password);
// BamConnection methods
String getJid();
boolean isClosed();
void close();
void setStreamHandler(BamStream handler);
void message(String to, Serializable value);
Serializable queryGet(String to, Serializable query);
Serializable querySet(String to, Serializable query);
void queryGet(String to, Serializable query, BamQueryCallback callback);
void querySet(String to, Serializable query, BamQueryCallback callback);
void presence(Serializable data);
void presence(String to, Serializable data);
void presenceUnavailable(Serializable data);
void presenceUnavailable(String to, Serializable data);
void presenceProbe(String to, Serializable data);
void presenceSubscribe(String to, Serializable data);
void presenceSubscribed(String to, Serializable data);
void presenceUnsubscribe(String to, Serializable data);
void presenceUnsubscribed(String to, Serializable data);
void presenceError(String to, Serializable data, BamError error);
BamStream getBrokerStream();
}
Protocol(Packet) APIBamStream
Applications will implement The presence methods implement the specialized subscription and presence messages. IM applications use presence messages to announce availability to people in a buddy list (roster). Publish/Subscribe applications can also use subscription packets to subscribe and unsubscribe from the publishing service. BamStream
package com.caucho.bam;
public interface BamStream {
{
public void message(String to, String from, Serializable value);
public void messageError(String to, String from, Serializable value,
BamError error);
boolean queryGet(long id, String to, String from, Serializable query);
boolean querySet(long id, String to, String from, Serializable query);
void queryResult(long id, String to, String from, Serializable value);
void queryError(long id, String to, String from, Serializable query,
BamError error);
void presence(String to, String from, Serializable data);
void presenceUnavailable(String to, String from, Serializable data);
void presenceProbe(String to, String from, Serializable data);
void presenceSubscribe(String to, String from, Serializable data);
void presenceSubscribed(String to, String from, Serializable data);
void presenceUnsubscribe(String to, String from, Serializable data);
void presenceUnsubscribed(String to, String from, Serializable data);
void presenceError(String to, String from, Serializable data,
BamError error);
}
Service APIsBamBroker
For all that responsibility, the API is fairly simple. The
BamBroker
package com.caucho.bam;
public interface BamBroker extends BamConnectionFactory
{
BamStream getBrokerStream();
void addService(BamService service);
void removeService(BamService service);
void addServiceManager(ServiceManager manager);
}
BamService
The key methods are The additional methods are used for specialized applications like instant messaging and multiuser-chat, to manage clients logging in. BamService
package com.caucho.bam;
public interface BamService
{
public String getJid();
public BamStream getAgentStream();
public BamStream findAgent(String jid);
public void onAgentStart(String jid);
public void onAgentStop(String jid);
public BamStream getAgentFilter(BamStream stream);
public BamStream getBrokerFilter(BamStream stream);
}
BamServiceManager
BamServiceManager
package com.caucho.bam;
public interface BamServiceManager
{
public BamService findService(String jid);
}
| |||||||||||||||||||||||||||||||||