![]() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| documentation examples changes overview quick start installation command-line configuration guide: admin admin amber bam caching clustering database deployment ejb 3.0 embedding filters hessian 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 | bam
BAM (Brokered Agent Messaging) is a simplified messaging API designed around federated, addressable services, model-based messages, and supports both message and rpc-style communication. As an abstraction of the Jabber protocol, it supports instant messaging, queued (SEDA) services, publish/subscribe, interactive games, and event notification applications. BAM supports multiple protocols including local messaging, Hessian protocol and XMPP (Jabber).
Queued MessagesSending a message from a client to a named service is a simple and important use of BAM. Typical applications include message queuing, chat text, game updates, Atom/RSS updates, pub/sub messaging, and event notification for administration consoles. In out example, a servlet sends a message to an internal logging service. The sending agent calls ![]() The message can be any serializable object appropriate to the
service, either a custom Java model bean, or an XML string, or a
defined protocol message, like a Jabber IM message. This flexibility
lets services define messages appropriate to the application, and
avoids tying the service into knots trying to conform to a restricted
encoding like SOAP. If remoting is used, the remoting protocol might
restrict the possible messages. HMTP (Hessian) will allow any
serializable object, while XMPP (Jabber) is restricted to XML
and Since BAM messages are addressible and routed through the broker,
clients have flexibility in choosing their destination at runtime.
BAM messages use a JID (Jabber ID) for addressing, which looks like
![]() Writing a BAM client involves the following steps:
In the example, the servlet creates a Example: TestClient.java
package example;
import javax.servlet.*;
import com.caucho.bam.BamClient;
public class TestClient extends GenericServlet
{
private BamClient _client = new BamClient();
public void service(ServletRequest req, ServletResponse response)
{
_client.message("test@", "Hello, world!");
}
}
Writing a BAM message/queuing service involves the following steps:
By configuring Example: LogService.java
package example;
import com.caucho.bam.SimpleBamService;
import java.io.Serializable;
import java.util.logging.*;
public class LogService extends SimpleBamService
{
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 BAM configuration the service configured with Resin IoC. Example: WEB-INF/resin-web.xml
<web-app xmlns="http://caucho.com/ns/resin">
<bam-service name="test" class="example.LogService"/>
</web-app>
Client queryGet (RPC) exampleRemote calls in BAM can query or update a service based on the type of the query message. Since the query is typed, a service can be defined by the set of query types it understands and even mixin multiple capabilities, like implementing both a chat and a pub/sub service. In this example, we just query a service for some basic information. ![]() We'll use a remote client to show how BAM can be used as a remote
service as well a local organization. When you create a Example: 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();
}
}
To implement the server side of an RPC call, the service implements
For a query, the service must always send a QueryResult message or a QueryError message with the same back to the caller to match responses to the calls. If the service understands the query, it will send a result message and return true. If it does not understand the query, it will return false, which tells the broker to send a query error message. ![]() The matches responses to the corresponding queries. Since BAM is a bidirectional streaming architecture, queries can be unordered and start from either direction. The turns this unordered mess into a coherent request-response pattern for RPC-style calls. Example: TestService.java
package example;
import com.caucho.bam.SimpleBamService;
import com.caucho.bam.annotation.QueryGet;
public class TestService extends SimpleBamService
{
@QueryGet
public boolean testQueryGet(long id, String to, String from,
TestQuery query)
{
getBrokerStream().sendQueryResult(id, to, from, "hello response");
return true;
}
}
The configuration for a service now has two components:
The BAM service itself does not know or care that it's being called remotely. The remote HMTP servlet exists only so the remote client can login to the local broker. Example: WEB-INF/resin-web.xml
<web-app xmlns="http://caucho.com/ns/resin">
<bam-service name="test" class="example.TestService"/>
<servlet-mapping url-pattern="/hmtp"
servlet-class="com.caucho.hemp.servlet.HempServlet"/>
</web-app>
Applications using BAM will generally follow a Brokered Agent Messaging pattern, 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 ![]() 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, . ![]() BAM resources all have unique identifiers called JIDs (Jabber IDs), which look and act like extended email addresses. Because IM applications can have multiple connections for the same user, each address has an optional resource providing a unique name for the connection. The id looks like: JID format @/
The and are optional.
<bam-service> configures a <bam-service> can be used as a simple queueing service, replacing JMS queues and ejb-message-bean. By default the queue is consumed with a single thread. Unlike EJB message driven beans, <bam-service> uses a single instance like a servlet, not a pool of instances. The bean has full access to Resin-IoC capabilities, including dependency injection, transaction attributes, and aspect interception.
<bam-service> schema
element ejb-message-bean {
(class | uri)
& init?
& name?
& thread-max?
}
Resin includes a number of predefined BAM services for JMS compatibility,
logging, and mail messages. The services are all configured with
JMSThe JMS service forwards BAM messages to a JMS queue, wrapping each message in a JMS ObjectMessage. Applications can use this BAM to JMS bridge to queue messages from a Java client. Example: JMS configuration
<web-app xmlns="http://caucho.com/ns/resin">
<jms-connection-factory name="jms_cf" uri="resin:"/>
<jms-queue name="queue" uri="memory:"/>
<bam-service name="jms" uri="caucho.jms:">
<init>
<connection-factory>${jms_cf}</connection-factory>
<queue>${queue}</queue>
</init>
</bam-service>
</web-app>
LoggingThe logging service adds BAM messages to a java.util.logging Logger. An application could use the logging service as a chat-room recording or debugging service.
Example: Log configuration
<web-app xmlns="http://caucho.com/ns/resin">
<bam-service name="log" uri="caucho.log:">
<init>
<name>com.foo.chat</name>
<level>info</level>
</init>
</bam-service>
</web-app>
The mail service sends BAM messages to an email address. This can be used to notify any issues with an application that may need administration.
Example: Mail configuration
<web-app xmlns="http://caucho.com/ns/resin">
<bam-service name="mail" uri="caucho.mail:">
<init>
<to>bam@foo.com</name>
<subject>BAM Notification</subject>
<properties>
mail.from=bamservice@foo.com
</properties>
</init>
</bam-service>
</web-app>
PHPThe php service configures a PHP script as a service handler.
Example: PHP configuration
<web-app xmlns="http://caucho.com/ns/resin">
<bam-service name="php" uri="caucho.php:">
<init>
<script>WEB-INF/php/php-service.php</script>
</init>
</bam-service>
</web-app>
Example: WEB-INF/php/php-service.php
<?php
bam_message($to, $from, $message)
{
resin_debug($message);
}
bam_dispatch();
?>
Local (JVM calls)Local JVM applications can use BAM to organize internal applications like queuing consumers to replace JMS, or service-oriented architectures. Because BAM passes messages by reference, it avoids the cpu and memory overhead of serialization, improving performance. The local clients can take advantage of BAM's federated addressing and can send messages to foreign machines while logging into the local broker. HMTP (Hessian)HMTP (Hessian Message Transport Protocol) is a streaming mode of the Hessian protocol supporting BAM. Since Hessian can serialize any Java object, it can support all of BAM's capabilities for remote services and since Hessian has been ported to many languages including Flash/ActionScript, multilanguage clients can use BAM directly though HMTP. XMPP (Jabber)BAM 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 BAM is designed to follow XMPP, its architecture and protocols are essentially identical until the very lowest layer. 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
BAM provides three categories of packets: messages, queries (rpc), and presence announcements. A queuing or messaging application might only use message packets, while a pub/sub service might use messages and queries. Chat, conference room, and monitoring software will use presence messages to announce joining or startup events. Messages are unidirectional fire-and-forget packets. They can be used for queuing systems as a replacement JMS queues. 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 BAM, the payload can be any serializable value. Example messages could be IM text messages, queued tasks, administration console graph, game updates, or updated stock quotes. Since BAM 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.
Applications use
Client APIBamClient
BamClient
package com.caucho.bam;
public class BamClient implements BamConnection
{
public BamClient();
public BamClient(String uid, String password);
public BamClient(String uid, String password, String resource);
String getJid();
// message
void message(String to, Serializable value);
// rpc
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);
// presence
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);
// callback handler for receiving messages
void setStreamHandler(BamStream handler);
// raw stream to return rpc responses
BamStream getBrokerStream();
}
BamConnection
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();
// message
void message(String to, Serializable value);
// rpc
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);
// presence
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);
// callback handler for receiving messages
void setStreamHandler(BamStream handler);
// raw stream to return rpc responses
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 String getJid();
// message
public void message(String to, String from, Serializable value);
public void messageError(String to, String from, Serializable value,
BamError error);
// rpc
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);
// presence
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 boolean startAgent(String jid);
public boolean stopAgent(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);
}
SimpleBamServiceSimpleBamService
package com.caucho.bam;
abstract public class SimpleBamService implements BamService, BamStream
{
public String getJid();
// message
public void message(String to, String from, Serializable value);
public void messageError(String to, String from, Serializable value,
BamError error);
// rpc
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);
// presence
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);
// BamService methods
public BamStream getAgentStream();
public boolean startAgent(String jid);
public boolean stopAgent(String jid);
public void onAgentStart(String jid);
public void onAgentStop(String jid);
public BamStream getAgentFilter(BamStream stream);
public BamStream getBrokerFilter(BamStream stream);
}
annotationsannotations
package com.caucho.bam.annotation;
public @interface Message {}
public @interface MessageError {}
public @interface QueryGet {}
public @interface QuerySet {}
public @interface QueryResult {}
public @interface QueryError {}
public @interface Presence {}
public @interface PresenceProbe {}
public @interface PresenceUnavailable {}
public @interface PresenceSubscribe {}
public @interface PresenceSubscribed {}
public @interface PresenceUnsubscribe {}
public @interface PresenceUnsubscribed {}
public @interface PresenceError {}
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||