> For the complete documentation index, see [llms.txt](https://docs.rumi.systems/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rumi.systems/rumi-core/concepts/messaging-model/solace-binding.md).

# Solace Binding

The Solace binding provides native integration with Solace PubSub+ message brokers for high-performance, low-latency messaging in Rumi microservices.

## Overview

The Solace binding uses Solace's native APIs (not JMS) to achieve optimal performance with Solace PubSub+ message brokers. It supports both Solace PubSub+ hardware appliances and software brokers.

For configuration details, see [Solace Binding Configuration](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections/solace-binding.md).

## JNI vs Java Implementation

The Solace binding includes both a Java-based and JNI-based implementation:

### Java Binding (JCSMP)

* Uses Solace's **JCSMP** (Java Client Software Messaging Protocol) API
* Works on **all platforms** supported by Rumi
* **Not zero-garbage** but provides good performance
* Fully managed Java implementation

### JNI Binding (CCSMP)

* Uses Solace's **CCSMP** (C Client Software Messaging Protocol) API via JNI
* **Linux only**
* Supports **zero-garbage messaging** in steady state
* Maximum performance for latency-sensitive applications

### Key Differences

| Aspect                         | Java (JCSMP)                     | JNI (CCSMP)                                    |
| ------------------------------ | -------------------------------- | ---------------------------------------------- |
| Platforms                      | All                              | Linux only                                     |
| Garbage                        | Non-zero                         | Zero in steady state                           |
| UnhandledMessageEvent topic    | Sent topic name                  | Subscription name                              |
| UnhandledMessageEvent sequence | Appliance-wide sequence number   | Session-specific sequence number               |
| Pass-through properties        | Properties starting with `jcsmp` | Properties starting with `FLOW_` or `SESSION_` |

{% hint style="info" %}
The binding rationalizes some common properties between the two implementations, but if using properties specific to JCSMP or CCSMP that aren't rationalized, consider whether JNI is enabled.
{% endhint %}

## Solace Topic Format

Solace topics are hierarchical with each level separated by `/` character. Topics cannot exceed 250 characters in length.

**Example topics**:

* `orders/events/MSFT`
* `market-data/trades/US/APPL`
* `alerts/critical/region1`

For more details on Solace topics, refer to [Solace documentation](https://docs.solace.com/Features/SMF-Topics.htm).

## Wildcard Topics

Solace supports wildcard characters for matching multiple topics in [channel filters](/rumi-core/guides/developing-applications/configuring-messaging/registering-message-interest.md). Wildcards are **not** applied to sent topics (treated as literals).

The Solace binding preserves these wildcards when `nv.sma.cleanchannelfilter=true`:

| Wildcard | Description                                                          | Example                                                                                                                                                                                                                                  |
| -------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `*`      | Matches 0 or more characters at end of topic level                   | <p><code>orders/gin\*</code> matches:<br>- <code>orders/gin</code><br>- <code>orders/ginseng</code><br><br>Does not match:<br>- <code>orders</code><br>- <code>orders/in</code><br>- <code>orders/gin/foo</code></p>                     |
| `>`      | Matches multiple topic levels. Must be only character in last level. | <p><code>orders/></code> matches:<br>- <code>orders/events/MSFT</code><br>- <code>orders/updates/APPL</code><br>- <code>orders/cancels/US/IBM</code><br><br>Does not match:<br>- <code>orders</code><br>- <code>order-updates</code></p> |

### Topic Routing Example

The following example demonstrates dynamic topic resolution with wildcards:

```xml
<buses>
  <bus name="trading" descriptor="solace://solacehost:55555&topic_starts_with_channel=false">
    <channels>
      <channel name="orders">
        <qos>Guaranteed</qos>
        <key>${InitiatingClient::NONE}/${Region::NONE}/${Symbol::NONE}</key>
      </channel>
    </channels>
  </bus>
</buses>

<services>
  <service name="order-manager">
    <messaging>
      <buses>
        <bus name="trading">
          <channels>
            <channel name="orders">
              <filter>Region="US";Symbol=A*|MSFT</filter>
            </channel>
          </channels>
        </bus>
      </buses>
    </messaging>
  </service>

  <service name="client">
    <messaging>
      <buses>
        <bus name="trading" join="true">
          <channels>
            <channel name="orders" join="false" />
          </channels>
        </bus>
      </buses>
    </messaging>
  </service>
</service>
```

The order-manager application uses filter `Region="US";Symbol=A*|MSFT`, creating these subscriptions:

* `*/US/A*` - Any client, US region, symbols starting with A
* `*/US/MSFT` - Any client, US region, MSFT symbol

**Matching messages**:

```
{InitiatingClient: "CLIENTA", Region: "US", Symbol: "MSFT"} → CLIENTA/US/MSFT → matches */US/MSFT
{InitiatingClient: "CLIENTA", Region: "US", Symbol: "APPL"} → CLIENTA/US/APPL → matches */US/A*
{Region: "US", Symbol: "MSFT"} → NONE/US/MSFT → matches */US/MSFT
```

**Non-matching messages**:

```
{InitiatingClient: "CLIENTA", Region: "US", Symbol: "IBM"} → CLIENTA/US/IBM → no match
{Symbol: "IBM"} → NONE/NONE/IBM → no match
```

## Message Metadata Version

When messages are sent through the Solace binding, SMA (Simple Messaging API) metadata is included to help receivers deserialize and dispatch messages.

By default, the Solace binding sends messages using **V1 metadata** to ensure compatibility with older receivers (pre-Rumi 1.8.396). The difference:

* **V1**: Receivers must introspect serialized message content to determine type
* **V2**: Message type is encoded in metadata (more efficient)

{% hint style="info" %}
If all downstream receivers use Rumi 1.8.396 or later, set `sma_metadata_version=2` as a Solace binding property for more efficient metadata.
{% endhint %}

## Sending and Receiving from External Applications

This section describes integration with non-Rumi applications over Solace. This is an uncommon use case - see [Sending Messages](/rumi-core/guides/developing-applications/authoring-user-code/message-processing/processing-messages/sending-messages.md) for Rumi-to-Rumi messaging.

{% hint style="info" %}
These examples use ADM-generated messages for convenience, but this is not required. Messages can be encoded in any format (e.g., Protobuf, Xbuf) and encoded/decoded manually.
{% endhint %}

### Sending Messages to Rumi

The Solace binding transports encoded messages in a `BytesMessage` with:

* **Message data**: Serialized message payload
* **x-sma-metadata property**: Serialized `MessageMetadata`

**Example using Solace JCSMP client**:

```java
// populate a message
OrderEventMessage orderEvent = OrderEventMessage.create();
orderEvent.setOrderId(1);

// serialize the payload to a byte buffer
byte[] serializedPayload = orderEvent.serializeToByteArray();

// prepare message metadata
MessageMetadata metadata = MessageMetadataFactory.getInstance().createMessageMetadata();
metadata.serializeV2(orderEvent.getMessageEncodingType(),
                     orderEvent.getVfid(),
                     orderEvent.getType(),
                     0,  // message sender id
                     0,  // message flow
                     0,  // message sequence number (unsequenced)
                     -1, // channel id (unspecified)
                     XString.create("order-events"));
byte[] serializedMetadata = metadata.serializeToByteArray();

// prepare solace message
com.solacesystems.jcsmp.BytesMessage message = producer.createBytesMessage();
message.setData(serializedPayload);
message.setDeliveryMode(DeliveryMode.DIRECT);
SDTMap props = producer.createMap();
if (serializedMetadata != null) {
  props.putBytes("x-sma-metadata", serializedMetadata);
}
message.setProperties(props);

// send
com.solacesystems.jcsmp.Topic topic = producer.createTopic("order-events");
producer.send(message, topic);

// dispose
orderEvent.dispose();
metadata.dispose();
```

### Receiving Messages from Rumi

**Example using Solace JCSMP client**:

```java
public void onMessage(BytesXMLMessage message) {
  if (!(message instanceof BytesMessage)) {
    handleNonTalonMessage(message);
    return;
  }

  BytesMessage bytesMessage = (BytesMessage) message;

  // extract sma metadata
  final SDTMap props = bytesMessage.getProperties();
  ByteBuffer serializedMetadata = null;
  if (props != null) {
    byte[] metadataBytes = props.getBytes("x-sma-metadata");
    if (metadataBytes != null) {
      serializedMetadata = ByteBuffer.wrap(metadataBytes);
    }
  }

  if (serializedMetadata == null) {
    handleNonTalonMessage(message);
    return;
  }

  // extract metadata fields
  final byte encodingType = MessageMetadata.getMessageEncodingType(serializedMetadata);
  final short vfid = MessageMetadata.getMessageViewFactory(serializedMetadata);
  final short vtype = MessageMetadata.getMessageViewType(serializedMetadata);

  // extract payload
  byte[] payloadBytes = bytesMessage.getData();
  Object payload = payloadBytes;
  if (encodingType == MessageView.ENCODING_TYPE_JSON) {
    payload = new String(payloadBytes);
  }

  // lookup factory (assumes view factories already registered)
  MessageViewFactory factory = viewFactoryRegistry.getMessageViewFactory(vfid);
  if (factory != null) {
    MessageView view = factory.wrap(vtype, encodingType, payload);
    if (view instanceof OrderEventMessage) {
      handleOrderEvent((OrderEventMessage) view);
    }
  }
  else {
    handleNonTalonMessage(message);
  }
}
```

## See Also

* [Solace Binding Configuration](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections/solace-binding.md) - Configuration reference
* [Messaging Model](/rumi-core/concepts/messaging-model.md) - Overview of Rumi messaging
* [Configuring Bus Connections](/rumi-core/guides/developing-applications/configuring-messaging/configuring-bus-connections.md) - General bus configuration
* [Solace PubSub+](https://solace.com/products/message-routers) - Solace message broker information
