The Sapo-Broker 4.0 User Guide

High performance distributed messaging framework

4.0.15


Table of Contents

Preface
1. Introduction
2. Sapo-Broker features
2.1. Supported broker topologies
2.2. Message publication and subscription
3. License
1. User Guide
1. Getting started
1.1. Requirements
1.2. Installation procedure
1.3. Running the samples
2. Client libraries
2.1. Client compatibility matrix
2.2. Client usage samples in different languages
3. System topics
3.1. Statistics
3.2. Faults
4. Configuration
4.1. Global configuration
2. Advanced topics
1. Access control
1.1. Access control evaluation criteria
1.2. An annotated sample
2. Authentication extensibility model
2.1. Configuration
2.2. Relevant classes
2.3. Implemented authentication modules
2.4. SSL configuration
3. Sapo-Broker reliability
3.1. Solution 1 – Client persistence
3.2. Solution 2 – Global acknowledge record
3. Community and Support
1. Building from the source
2. Still have questions?

List of Figures

2.1. Authorization Provider relevant types

List of Tables

1.1. Client library compatibility matrix
2.1. Authentication types

List of Examples

1.1. Sample Java producer
1.2. Sample Java consumer
1.3. Sample C#/.NET publisher
1.4. Sample C#/.NET consumer
1.5. Sample Python consumer
1.6. Sample Python producer
1.7. Sample Perl producer
1.8. Sample Perl consumer
1.9. Sample PHP producer
1.10. Sample PHP consumer
2.1. Security policy configuration sample
2.2. Authorization providers configuration
2.3. Database creation scripts
2.4. Agent's SSL sample configuration

Preface

1. Introduction

Sapo-Broker is a distributed messaging framework. Among other features, it provides minimal administration overhead, Publish-Subscribe, Request-Reply and Point-to-Point messaging, guaranteed delivery and wildcard subscriptions.

Sapo-Broker is written in Java and has client libraries for Java, Perl, Python, PHP, .NET and C.

2. Sapo-Broker features

2.1. Supported broker topologies

Sapo-Broker can be used as a single instance or in a multi-node cluster. Although a single agent instance could be used in small working sets, a multiple agent (network) design would better take advantage of Sapo-Broker, namely in system availability and throughput, with client failover and distributed workload, respectively. In a multi-node scenario all agents are interconnected in what is known as agent cloud. It is not yet possible to communicate between different clouds.

2.2. Message publication and subscription

Like Java Message Service (JMS) we use the terms "TOPIC" to refer Publish-Subscribe messaging and "QUEUE" to designate Point-to-Point messaging.

There is no way, nor need, to create a "QUEUE" or "TOPIC" before any client can consume or produce messages. The "QUEUE" or "TOPIC" is created on the fly, as clients subscribe to it. Note that a typo in the "QUEUE" name, in the subscription operation, can lead to two different queues, in a way that the consumer will try to read from a forever empty queue, and the producer will write to a never read queue.

2.2.1. Topics

Messages published to "TOPIC"s are delivered to any consumer which has a subscription name that matches the Destination name (topic subscriptions can have regular expressions). There are no provisions for message persistence when using Topics and slow consumer will force the broker to discard messages.

2.2.2. Queues

Queues implement reliable load balancing (a message is delivered to only one consumer). Messages are persisted to disk and are only removed from the Queue after the broker receives an acknowledgement, unacknowledged messages are redelivered after 2 minutes.

2.2.2.1. Virtual Queues

Events published as TOPIC messages are not persisted and QUEUE messages once consumed by a client can not be consumed by another, this means that if there are two systems interested in the same messages they can not subscribe to the same Queue.

To overcome these handicaps Sapo-Broker supports a VIRTUAL_QUEUE Destination Type. When clients subscribe to a topic as a Virtual Queue an ordinary queue is created and all topic messages whose Destination Name match the subscription are stored in the created queue.

The subscription for a Virtual Queues has the form:

[private name]@[topic subscription pattern]

An example could be: myVirtualQueue@/topic/.*

Please note that the private names must be unique. Also note that can exist several Virtual Queues for the same topic or subscription pattern.

3.  License

Sapo-Broker is distributed under the BSD license.

Chapter 1. User Guide

1. Getting started

1.1. Requirements

In order to start using the Sapo-Broker you just need to have an installation of Java SDK 1.6 or higher and the "java" command in your PATH. You can quickly check for java version by opening a shell and typing:

java -version

1.2. Installation procedure

The installation process is very easy. These are the required steps:

1.3. Running the samples

From a shell type:

./bin/broker.sh

At this moment you should have an instance of the Sapo-Broker up and running. There is nothing stopping you from using a single instance of Sapo-Broker, but since one of it's main goals is to be used in a distributed fashion you really want to have at least two instances to play with, read the User Guide to know how to setup and connect multiple instances.

The distribution includes a couple of sample scripts that illustrate a common use case for Sapo-Broker, event propagation, among others. These scripts include a producer that sends messages with 100 random alpha numeric characters at the rate of one per second and a consumer that prints the message content to the console every time it receives a message.

To run the producer open a shell and type:

./clients/java/bin/producer.sh

To run the consumer:

./clients/java/bin/consumer.sh

Besides the Linux scripts, there are also available windows batch files counterparts.

2. Client libraries

Given the environment where Sapo-Broker was developed, somewhere where several projects are interconnected, written in many different programming languages it was absolutely crucial to support several broker client libraries written in various programming languages.

For now, not all take advantaged of the features implemented in Sapo-Broker 3, but are still compatible through the use of a legacy port.

The supported programming languages or platforms are: Java, .NET, Perl, Python, PHP, Ruby, C and Erlang. These libraries may be in different states of maturity.

2.1. Client compatibility matrix

To help the choice of library and enlighten about what features are supported by witch libraries a client compatibility matrix is presented.

Table 1.1. Client library compatibility matrix

 Java.NETPerlPythonPHPC
SOAP encodingxxxxxx
ProtoBuf encoding (binary)xoooox
Thrift encoding (binary)xxooox
SSL supportxxooox
Client authenticationxxoooo
Client authentication with Sapo STSxxoooo
Client failoverxxxxxx

Specific information about each client and their features, as well some samples, should be available. Poke around.

2.2. Client usage samples in different languages

In this section examples of publishing and consuming messages are going to be presented for several languages, namely Java , C# (.NET) , Python , Perl , PHP.

2.2.1. Java samples

Example 1.1. Sample Java producer

BrokerClient bk = new BrokerClient("127.0.0.1", 3323);
String msg = RandomStringUtils.randomAlphanumeric(messageLength);
NetBrokerMessage brokerMessage = new NetBrokerMessage("Hello World!!".getBytes("UTF-8"));
bk.publishMessage(brokerMessage, "/topic/foo");

Example 1.2. Sample Java consumer

public class Consumer implements BrokerListener
{
	public static void main(String[] args) throws Throwable
	{
		Consumer consumer = new Consumer();
		BrokerClient bk = new BrokerClient("localhost", 3323);
		NetSubscribe subscribe = new NetSubscribe("/topic/foo", DestinationType.TOPIC);
		bk.addAsyncConsumer(subscribe, consumer);
	}

	@Override
	public boolean isAutoAck()
	{
		return false;
	}

	@Override
	public void onMessage(NetNotification notification)
	{
		byte[] payload = notification.getMessage().getPayload();
		System.out.println(String.format("Message destination: %s%n Payload: %s", notification.getDestination(), new String(payload)));
	}
}

2.2.2. C#/.NET samples

Example 1.3. Sample C#/.NET publisher

BrokerClient brokerClient = new BrokerClient(new HostInfo("localhost", 3323));
NetBrokerMessage brokerMessage = new NetBrokerMessage(System.Text.Encoding.UTF8.GetBytes("Hello World!!"));
brokerClient.Publish(brokerMessage, "/topic/foo");

Example 1.4. Sample C#/.NET consumer

BrokerClient brokerClient = new BrokerClient(HostInfo("localhost", 3323));
Subscription subscription = new Subscription("/topic/foo", NetAction.DestinationType.TOPIC);
subscription.OnMessage += delegate(NetNotification notification)
{
	 System.Console.WriteLine("Message received: {0}", System.Text.Encoding.UTF8.GetString(notification.Message.Payload));
};
brokerClient.Subscribe(subscription);

2.2.3. Python samples

Example 1.5. Sample Python consumer

#!/usr/bin/env python

from Broker.Messages import Message, Publish
from Broker.Transport import TCP, UDP
from Broker.Codecs import Codec
from Broker.Clients import Minimal

server='localhost'
destination = '/python/tests'
destination_type = 'QUEUE'
N=10000

broker = Minimal(codec=Codec(), transport=TCP(host=server))

for n in xrange(N):
    message = Message(payload='Message number %d' % n)
    publish = Publish(destination=destination, destination_type=destination_type, message=message)
    broker.send(publish)

Example 1.6. Sample Python producer

#!/usr/bin/env python


from Broker.Messages import Message, Subscribe, Acknowledge
from Broker.Transport import TCP, UDP
from Broker.Codecs import Codec
from Broker.Clients import Minimal

server='localhost'
destination = '/python/tests'
destination_type = 'QUEUE'

broker = Minimal(codec=Codec(), transport=TCP(host=server))

broker.send(Subscribe(destination=destination, destination_type=destination_type))
for n in xrange(N):
    message = broker.receive()
    broker.send(Acknowledge(message_id=message.message.id, destination=message.destination))
    print message.message.payload

2.2.4. Perl samples

Example 1.7. Sample Perl producer

use SAPO::Broker::Clients::Simple;

use strict;
use warnings;

my $broker = SAPO::Broker::Clients::Simple->new();
my %options = (
	'destination_type' => 'QUEUE',
	'destination' => '/perl/tests',
);

my $N = $ARGV[0] || 100;

for my $n (1..$N){
	$broker->publish(%options, 'payload' => "payload $n");
}

Example 1.8. Sample Perl consumer

use lib ('../lib');

use SAPO::Broker::Clients::Simple;
use Data::Dumper;

use strict;
use warnings;

my $broker = SAPO::Broker::Clients::Simple->new();
my %options = (
	'destination_type' => 'QUEUE',
	'destination' => '/perl/tests',
	'auto_acknowledge' => 1
);

$broker->subscribe(%options);

my $N = $ARGV[0] || 100;

for my $n (1..$N){
	my $message = $broker->receive();
	print Dumper($message);
}

2.2.5. PHP samples

Example 1.9. Sample PHP producer

#!/usr/bin/env php
<?php

$broker = broker_init("127.0.0.1", 3323, 0, 1);
$msg = "Hello, world!";
broker_publish($broker, "/test/foo", $msg, strlen($msg));
broker_destroy($broker);
?>

Example 1.10. Sample PHP consumer

#!/usr/bin/env php
<?php

$broker = broker_init("127.0.0.1", 3323, 0, 1);
$ret = broker_subscribe_topic($broker, "/test/foo");

// consume without auto-ack
while (1) {
	if (($msg = broker_receive($broker, 1000, true)) !== false)
    	echo "Got message: " . print_r($msg, true) . "\n";
}

?>

3. System topics

Sapo-Broker agents publish several information regarding their internal state and events. This information is published to /system/[information type] topics.

In order to guarantee that only agents produce messages to these topics an access control entry should be defined in the default security policy.

3.1. Statistics

Agents publish statistics regarding the number of consumers subscribed in each topic and queue.

3.1.1. Topic consumers

Topic consumers are published in topic:

/system/stats/topic-consumer-count/#[topic subscription]#

The format of the published message, encoded using UTF-8, is:

[agent name]#[subscription]#[number of subscriptions]

3.1.2. Queue consumers

Queue consumers are published in topic:

/system/stats/queue-consumer-count/#[queue name]#

The format of the published message, encoded using UTF-8, is:

[agent name]#[queue name]#[number of subscriptions]

3.1.3. Queue Size

The number of messages by queue is published in topic:

/system/stats/queue-size/#[queue name]#

The format of the published message, encoded using UTF-8, is:

[agent name]#[queue name]#[number of messages]

3.1.4. Dropbox messages

The number of messages in the dropbox are published in topic:

/system/stats/dropbox/#[agent name]#

The format of the published message, encoded using UTF-8, is:

[agent name]#[dropbox location]#[number of messages]#[number of good messages]

3.2. Faults

When an unhandled exception occurs, and it's not I/O related, agents publish a fault error. The message content conforms to SOAP Fault message format.

Fault messages are published to topic:

/system/faults/[agent name]

A fault message can also be published when the maximum number of allowed queues or distinct topic subscriptions is reached . Here is a sample message content for each fault type:

The maximum number of queues (500) has been reached.

The maximum number of distinct subscriptions (500) has been reached.

4. Configuration

Sapo-Broker configuration has a two level scope, meaning that some configuration aspects are related to Broker agents and others are global, and, as such, are applicable to all agents in the same cloud.

4.1. Global configuration

There are three main configuration sections in global configuration file: Domain, Messages and Access Control. The later is divided in two parts as discussed in Access control section.

The "domain" element contains all agentes that are part of the broker cloud. For each agent an IP:PORT must be defined.

This would be the Domain section of a configuration used to setup a cloud with two nodes:

<domain>
	<peer>
		<ip>192.168.0.10</ip>
		<port>3315</port>
	</peer>
	<peer>
		<ip>192.168.0.11</ip>
		<port>3315</port>
	</peer>
</domain>

Keep in mind that the configuration must be the same for all cloud agents.

In the "messages" section it is possible to define the maximum message size, the maximum number of queues/subscriptions and default message TTL.

See the SSL Configuration guide for help on how to setup the SSL transport.

Chapter 2. Advanced topics

1. Access control

In order to protect sensitive information Sapo-Broker provides an access control mechanism. This mechanism is parameterized by a security policy in the global configuration file, in the element "security-policies". Each agent may have a specific security or it may inherit the default security policy. The default policy is named "default".

Security policies may be composed hierarchically, which means that a given security policy X can inherit from a policy Y adding or overriding  specific aspects to the policy. These policies may be associated with agents. If no security policy is to be used, then the element "security-policies" should be omitted for performance reasons.

A security policy is composed by an Access Control List (ACL), which is composed by entries. Each entry has an action (PERMIT or DENY), a destination type (TOPIC, QUEUE or VIRTUAL_QUEUE), a destination (such as "/topic/foo" or a regular expression) and a privilege (READ or WRITE). Destination types and privileges may be declared as lists which makes the ACL entry easier to read and declare. An entry is also composed by conditions that determine when the ACL entry should be applied. Conditions represent a binary evaluation result which determines the application of the related ACL entry. They can be of the following types:

  • ADDRESS – An IPv4 address and mask
  • ROLE – A text value with the role name
  • CHANNEL_TYPE – A type of security property (CONFIDENTIALITY, INTEGRITY and/or AUTHENTICITY) guaranteed by the transport channel
  • AND – A composed condition that evaluates to true if all the contained conditions are true
  • ALWAYS – The ACL entry is always applied

1.1. Access control evaluation criteria

Every agent as an associated security policy, except in the case of security policy absence in witch it defaults to "allow everything". In the case of policy inheritance, eventually with multiple degrees, a stack of entries is built with the most specific ACLs at the top and entries inserted by declaration order within each ACL. The result is an ordered entry stack where the most specific/priority come first, so evaluating a client request against the full access control is sequential evaluation of entries. The first entry witch results in a positive evaluation is applied.

The entry stack is optimized for evaluation, although maintaining the described model. (More on this we I have the time).

1.2. An annotated sample

In Security policy configuration sample a security configuration sample is presented. As said before, this configuration aspect is defined in global configuration file.

The sample presents a scenario where the default security policy mandates that no client can produce messages to topic or queue whose named begin with "/system/". The default policy also dictates that destination names beginning with "/private/" are forbidden for all clients. These security settings apply to all agents using the default security policy.

At the bottom of the sample a section (XML element) entitled "agents" is present. Here is the place to associate specific security policies with agents. The sample states that an agent named "agent007" must used the "anotherPolicy" security policy. This policy inherits from the default security, adds some entries to the ACL and in a specific case overrides the default policy. The added entries specify that message production to the queue named "/specialQueue" is denied to all except those clients that simultaneous possess the role "VIP_role" and are using a type of communication channel that offers confidentiality (such as SSL). The declaration details are self-explanatory.

Example 2.1. Security policy configuration sample

<security-policies>
   <policies>
      <policy policy-name="default">
         <acl>
            <entry action="DENY" destination-type="TOPIC QUEUE" destination="/system/.*"  privilege="WRITE">
               <condition condition-type="ALWAYS" />
            </entry>
            <entry action="DENY" destination-type="TOPIC QUEUE TOPIC_AS_QUEUE" destination="/private/.*"  privilege="WRITE READ">
               <condition condition-type="ALWAYS" />
            </entry>
         </acl>
      </policy>
      <policy policy-name="anotherPolicy" inherits="default">
         <acl>
            <entry action="DENY" destination-type="TOPIC QUEUE TOPIC_AS_QUEUE" destination="/private/.*"  privilege="READ">
               <condition condition-type="ADDRESS">
                  <address  mask="24">10.11.12.0</address>
               </condition>
            </entry>
            <entry action="PERMIT" destination-type="QUEUE" destination="/specialQueue"  privilege="WRITE">
               <condition condition-type="AND">
                  <condition condition-type="ROLE">
                     <role>VIP_role</role>
                  </condition>
                  <condition condition-type="CHANNELTYPE">
                     <channel-type>CONFIDENTIALITY</channel-type>
                  </condition>
               </condition>
            </entry>
            <entry action="DENY" destination-type="QUEUE" destination="/specialQueue"  privilege="WRITE">
               <condition condition-type="ALWAYS" />
            </entry>
         </acl>
         </policy>
         <policy policy-name="very special policy" inherits="special policy">
         <acl>
            <entry action="PERMIT" destination-type="TOPIC" destination="/topic/sensitive"  privilege="READ WRITE">
               <condition condition-type="AND">
                  <condition condition-type="ROLE">
                     <role>brk_writer_role</role>
                  </condition>
                  <condition condition-type="CHANNELTYPE">
                     <channel-type>CONFIDENTIALITY</channel-type>
                  </condition>
               </condition>
            </entry>       
           </acl>
         </policy>
      </policies>
     <agents>
         <agent agent-name="agent007">
            <agent-policy policy-name="anotherPolicy" />
         </agent>
      </agents>
</security-policies>
				

2. Authentication extensibility model

One of the possibilities of Sapo-Broker access control is based in client roles. The roles associated with clients are determined by pluggable components that comply to Sapo-Broker authentication extensibility model. This model defines:

  1. Configuration aspects that lead to component loading and usage
  2. A set of classes and interfaces that authentication modules implement and interact with

The modules are responsible for validating client credentials, directly or indirectly, and transform them in roles, if there are any associated with the authenticating client.

In order to support client authentication agents must provide an SSL channel and all role dependent communications (publications, notifications and subscriptions) should happen over a secure channel. This was a design decision. Given that client credentials are critical information they should not  be disclosed. One could argue that once authentication was established it could be used a clear channel, and therefore more efficient. However, the implementation approaches to address that design proved to be insecure or have a great deal of complexity (highly undesirable given the number of supported client libraries). Another reason drove this design: If something is so important that requires users to authenticate themselves and have a specified role, why allow this information to flow in clear? Moreover, the most significant burden of secure communication occurs during handshaking.

2.1. Configuration

Authentication modules, also called validation providers, are configured at the global configuration file. There are defined the full name of type implementing pt.com.broker.auth.AuthInfoValidator (more on this later) in the element class and some provider specific provider parameters, under provider-params. The class must be in agent's classpath in order to be loaded.

Each "provider-name" must be unique and known by both the broker agent and the client.

Table 2.1. Authentication types

SapoSTSAccepts SapoSTS (http://services.sapo.pt/Metadata/Service/STS?culture=EN) authentication tokens and obtains clients roles.
BrokerRolesDBUser name/password based authentication mechanism. Developed as as a proof-of-concept

Each of the available implementations are described later.

Example 2.2. Authorization providers configuration

<credential-validators>
   <credential-validator provider-name="SapoSTS">
      <class>pt.com.broker.auth.saposts.SapoSTSAuthInfoValidator</class>
      <provider-params>
         <sts>
            <sts-location>https://services.sapo.pt/STS/</sts-location>
            <sts-username>username</sts-username>
            <sts-password>password</sts-password>
         </sts>
      </provider-params>
   </credential-validator>
   <credential-validator provider-name="BrokerRolesDB">
      <class>pt.com.broker.auth.jdbc.JdbcAuthInfoValidator</class>
      <provider-params>
         <db-roles>
            <driver-class>org.postgresql.Driver</driver-class>
            <database-url>jdbc:postgresql://localhost/BROKER_ROLES</database-url>
            <database-username>username</database-username>
            <database-password>password</database-password>
         </db-roles>
      </provider-params>
   </credential-validator>
</credential-validators>
				

2.2. Relevant classes

At the heart of an authorization provider lies an implementation of the interface  pt.com.broker.auth.AuthInfoValidator. This interface and reaming relevant types are presented at Figure 5.

The initialization method of AuthInfoValidator gives the provider an opportunity to realize any actions  required to properly validate client's authorization information and translate that information in client roles. The ProviderInfo is used to inform the provider what is the name of the authentication type it will be performing, the class name (redundant) and the parameter specified in the configuration file. These arguments are supplied as an XML representation object.

The AuthInfoValidator.validate() method is invoked after the reception of an AuthInfo message. Depending on the authentication type the AuthInfo object contains a combination of a user identifier, an authentication token and list of user roles. The authentication token is a binary value, so, in case of representing a string such as a password must be UTF-8 encoded. In response to an authentication request a result is produced (AuthValidationResult) indicating whether the credentials are valid (a reason for failure should be supplied in case their not), and the list of roles associated with the user in case of valid credentials.

Figure 2.1. Authorization Provider relevant types

Authorization Provider relevant types

2.3. Implemented authentication modules

Currently, there are two authenticate modules implemented. One interacts with Sapo STS and it's used internally; the other was implemented as a proof-of-concept attesting broker authentication extensibility model's flexibility and provides user name-password database authentication.

2.3.1. Database based authentication

Sapo-Broker users may not have access to Sapo STS, and probably this would be the case, or they may need do provide their own authentication mechanisms. As a proof-of-concept it was implemented an authentication provider supported by a relational database. Here is the script used to create the tables. As shown, salts are not used and users passwords are store in clear, not their hashes. This approach won't cope with production environment requirements where the use of secure practices is (should be) mandatory. As said, this is just a proof-of-concept.

When using this authentication method, denoted by "BrokerRolesDB" authentication type, clients provide, as credentials, their user name and password.

The configuration of this provider, presented in Authorization providers configuration , takes the driver class to be loaded, which must be in agent's classpath, the connection URL and the credentials to access the database and respective tables. The authentication functionality is provided in sapo-broker-auth-jdbc.jar.

Example 2.3. Database creation scripts


CREATE TABLE users
(
 username varchar(50),
 password varchar(50)
);
ALTER TABLE users ADD PRIMARY KEY (username);

CREATE TABLE roles
(
 username varchar(50),
 role varchar(50)
);
ALTER TABLE roles ADD PRIMARY KEY (username, role);
ALTER TABLE roles ADD FOREIGN KEY (username) REFERENCES users(username);

					

2.4. SSL configuration

Sapo-Broker agents use SSL connections as message transport to client authentication messages and to support all communications that require authentication (role based access control). This type of connection is based on asymmetric cryptography operations and X.509 certificates. In order generate your own key pair and X.509 certificates a small tutorial on how to use SUN's keytool is provided.

2.4.1. Keytool tutorial

Generating a pair of keys to a new keystore:

keytool -genkey -validity 1000 -keystore ./certs2 -keyalg rsa -alias test -storepass serverkspw -keypass serverpw

View the stored keys:

keytool -list -alias test -keystore ./certs2

Export public key as an X.509 certificate

keytool -export -keystore ./certs2 -alias test -file testCert.cer

Importing a X.509 certificate o the default truststore (easier, but not recommend)

sudo keytool -import -file ./testCert.cer -keystore  /usr/lib/jvm/java-6-sun-1.6.0.10/jre/lib/security/cacerts

To a new keystore

keytool -import -file ./testCert.cer -keystore  ./clientKeystore

The generated keystore contains a public and private key. This keystore is meant to be used by the agent. A sample on how to configure SSL on the agent is presented here Code 4. This configuration element is specific to each agent, thus, it's not included in global.config file.

Example 2.4. Agent's SSL sample configuration

<ssl>
   <broker-ssl-port>3390</broker-ssl-port>
   <keystore-location>/home/server/agent/certs2</keystore-location>
   <keystore-password>serverkspw</keystore-password>
   <key-password>serverpw</key-password>
</ssl>
					

In order to clients trust agents certificates, during SSL handshaking, the agent's public key, through an X.509 certificate must be added to the trusted certificates client store. It can be added to the default truststore, but it's recommended that client applications specifically used a keystore containing the agent's certificate. This is the approach used by the Java client library.

3. Sapo-Broker reliability

The persistence and delivery model of queued messages, as discussed previously, protects the system against client failure but not against broker failure. If an agent crashes before receiving the client's Acknowledge the message will be delivered again. This violates the "once and only once" promisse but in real world scenarios is near impossible to guarantee 100% robustness. This is, of course, anomalous behaviour and should not happen in common operation. But if it does, and your application semantics dosen't allow for duplicate messages there are a couple of solutions to try.

3.1. Solution 1 – Client persistence

Clients, once they receive the message may persist it and send the respective Acknowledge with an Action Identifier. If the connection with the agent is lost or the Accept message is not received the message should not be processed, but if this not the case the client can process the message. Furthermore, the client is responsible for process locally persisted messages, and this behaviour should be ensured whenever a client restarts.

3.2. Solution 2 – Global acknowledge record

To guarantee that each produced message is processed only once a global acknowledge record may be implemented. After receiving a message, clients query the global acknowledge system to determine if the message was already processed, if not, they process it and afterwards send the due Acknowledge message and inform the global acknowledge system that a given message was already processed. The  last step may be only done if the acknowledge fails.

Chapter 3. Community and Support

1. Building from the source

You can fetch the latest version of the Sapo-Broker source code from our subversion server. For checking out the code use the svn command line client as follows:

svn co svn://softwarelivre.sapo.pt/broker/

To build, go to agents directory and type (you must have ant installed):

ant build-dist

2. Still have questions?

If you have unanswered questions send us an email to the project mailing listhttp://listas.softwarelivre.sapo.pt/mailman/listinfo/broker.