Use the WebLogic JMS Client for Microsoft .NET
Programming Considerations
These sections provide programming considerations and best practices to use when creating a JMS .NET client application:
- Using WebLogic JMS Extensions
- Limitations of Using the WebLogic JMS .NET Client
- Exchanging Messages Between Different Language Environments
- Specifying the URL Format
- Implementing Security With the JMS .NET Client
- Configuring Logging and Debugging
- Understanding Socket and Threading Behavior
- Data Conversion Between Java and .NET
- Best Practices
Using WebLogic JMS Extensions
Table 4-1 lists the WebLogic JMS extensions that are supported in this release of the JMS .NET client. There are several ways that messaging can be configured:
- On the connection factory—This method often defines default configuration settings.
- Programmatically in the application using the API—Certain programming constructs may override the connection factory configuration.
- On the server—Certain settings may override both the connection factory and programmatic constructs.
In some cases, there are differences in the way that an extension is configured, or in the behavior, between a JMS .NET client and a Java client. For example, some extensions cannot be enabled programmatically using the JMS .NET API, and can only be enabled via configuration. The following table summarizes the differences. Additional details, if required, are provided in the subsequent sections.
Table 4-1 WebLogic JMS Extensions Supported in the JMS .NET Client Feature Configurable on Connection Factory Configurable on the Server Java API JMS .NET API Comments Distributed Destinations (Uniform and Weighted) For more information, see:
- “Using Distributed Destinations” in Programming WebLogic JMS
- “Configuring Distributed Destination Resources” in Configure WebLogic JMS
Yes Yes No No Flow Control Producers For more information, see: “Controlling the Flow of Messages on JMS Servers and Destinations” in Performance and Tuning Yes Yes No No Blocking producers during quota conditions For more information, see “Defining a Send Timeout on Connection Factories” in Performance and Tuning Yes Yes No No Foreign destinations for remote instances of WebLogic Server For more information, see “Configuring Foreign Server Resources to Access Third-Party JMS Providers” in Configure WebLogic JMS No Yes No No See Interoperating with Previous WebLogic Server Releases. Imported store-and-forward (SAF) destinations For more information, see “Imported SAF Destinations” in Configure WebLogic Store-and-Forward No Yes No No Redelivery limit For more information, see “Setting a Redelivery Limit for Messages” in Programming WebLogic JMS No Yes Yes No Redelivery delay For more information, see “Setting a Redelivery Delay for Messages” in Programming WebLogic JMS Yes No Yes No Error destinations For more information, see “Configuring an Error Destination for Undelivered Messages” in Programming WebLogic JMS No Yes No No WLDestination.getCreateDestinationArgument No No Yes Yes No Acknowledge Mode For more information, see “Using NO_ACKNOWLEDGE” in Programming WebLogic JMS No No Yes Yes Unit-of-Order For more information, see:
- “Using Message Unit-of-Order” in Programming WebLogic JMS
- “Tuning Applicatinos Using Unit-of-Order” in “Tuning WebLogic JMS” in Performance and Tuning
Yes Yes Yes Yes See Unit-of-Order Scheduled message delivery For more information, see “Setting Message Delivery Times” in Programming WebLogic JMS Yes Yes Yes Yes See Message Delivery Time. Asynchronous consumer messages maximum pipeline
- For more information, see: “Asynchronous Message Pipeline” in Programming WebLogic JMS
- “Tuning MessageMaximum” in Performance and Tuning
Yes No Yes No Message Compression For more information, see “Message Compression” in Programming WebLogic JMS Yes No Yes No See Message Compression. Quotas For more information, see “Defining Quota” in Performance and Tuning No Yes No No One-way message sends For more information, see “Using One-Way Message Sends For Improved Non-Persistent Messaging Performance” in Performance and Tuning Yes No No No See One-Way Message Sends. Acknowledge policy For more information, see “JMS Connection Factory: Configuration: Client” in the Administration Console Online Help Yes No No No Automatically include user-id as message property JMSXUserID Yes Yes No No See Include user-id as JMSXUserId. Get number of delivery attempts as message property JMSXDeliveryCount No No No No See Message Delivery Attempts.
Message Compression
In this release, automatic message compression is not supported for client sends between the JMS .NET client and the JMS .NET client host running on WebLogic Server. However, if the compression settings are set on the connection factory, message compression behavior between the .NET client host and the destination is the same as that of the Java client. The behavior is as follows:
- If the client host and destination run on different instances of WebLogic Server, then a sent message is automatically compressed on the client host.
- If the client host and destination run on the same instance of WebLogic Server, then no sent message compression will occur.
Compressed messages are decompressed by the JMS .NET client host on the server side when they are received by the .NET client.
For more information, see “Message Compression” in Programming WebLogic JMS
Unit-of-Order
The method used to specify Unit-of-Order (UOO) in the JMS .NET API differs from the Java API. To set Unit-of-Order in the JMS .NET API, add a string property named Constants.MessagePropertyNames.UNIT_OF_ORDER_PROPERTY_NAME to the message with the desired UOO.
For more information, see “Using Message Unit-of-Order” in Programming WebLogic JMS
Message Delivery Time
The method used to specify message delivery times in the JMS .NET API differs from the Java API. To set message delivery times in the JMS .NET API, add a property of type long named Constants.MessagePropertyNames.DELIVERY_TIME_PROPERTY_NAME to the message, where the value is the number of milliseconds in the future in which the message will be delivered.
One-Way Message Sends
Although you can configure one-way message sends on the connection factory, this behavior is not fully supported in the JMS .NET client. Messages sent as one-way sends will actually be two- way sends between the .NET client and the .NET client host, and one-way sends between the .NET client host and the JMS connection host.
Include user-id as JMSXUserId
The optional JMSXUserId system-generated message property on received messages specifies the credential of the original sender. To enable this property, configure the “Attach Sender Credential” attribute on destinations, distributed destinations, or templates, and configure the “Attach JMSXUserId” attribute on connection factories. To retrieve, call msg.GetStringProperty(Constants.MessagePropertyNames.USER_ID_PROPERTY_NAME).
Message Delivery Attempts
The JMSXDeliveryCount system-generated message property on received messages specifies the number of message delivery attempts. The first attempt is 1. To retrieve the value, call msg.GetIntProperty(Constants.MessagePropertyNames.DELIVERY_COUNT_PROPERTY_NAME.
Limitations of Using the WebLogic JMS .NET Client
The following sections describe the JMS features that are not supported in the JMS .NET client.
Unsupported JMS 1.1 Standard Features
In this release, the following JMS 1.1 standard features are not supported:
- Creating and closing temporary destinations (javax.jms.TemporaryQueue and javax.jms.TemporaryTopic). The JMS .NET client can still produce messages to temporary destinations created by a Java client if the destination objects are obtained from the JMSReplyTo header of received messages.
- javax.jms.QueueRequester and javax.jms.TopicRequester. (These helper classes are related to temporary destinations.)
- Queue browsers: javax.jms.QueueBrowser.
- Queue and Topic interfaces (QueueConnectionFactory, TopicConnectionFactory, QueueConnection, TopicConnection, QueueSession, TopicSession). These queue and topic interfaces are legacy JMS 1.0.2 interfaces that have been superseded by the JMS 1.1 common interfaces.
Unsupported JMS 1.1 Optional Features
In this release, the following JMS 1.1 optional features are not supported:
- XA interfaces (XAConnectionFactory, XAConnection, and XASession).
- Participation in global XA transactions (See Transactions).
- Connection Consumer and Server session pools (javax.jms.ConnectionConsumer, ServerSessionPool, and ServerSession). These are optional capabilities that have been superseded by Java EE MDBs, and are not supported by the WebLogic Java JMS client.
- MessageProducer.setDisableMessageTimestamp method. Note that the WebLogic JMS Java client ignores this method.
Unsupported WebLogic JMS Extensions
In this release, the following WebLogic JMS extensions are not supported:
- SSL
- HTTP tunneling
- SAF Client—See “Reliably Sending Messages Using the JMS SAF Client” in Programming Stand-alone Clients
- Multicast Subscribers—See “Using Multicasting with WebLogic JMS” in Programming WebLogic JMS
- Automatic Reconnect—See “Automatic JMS Client Failover” in Programming WebLogic JMS
- Unit-of-Work—If a .NET client attempts to set a UOW property on a message, a Weblogic.Messaging.MessageException is generated. In addition, a .NET consumer cannot receive UOW messages with deserializable content that are sent by a Java client. In this case, the consumer gets a MessageFormatException if it calls the ObjectMessage.getObject() method on the ObjectMessage. Note that while Unit-of-Work is not supported, the more commonly used Unit-of-Order extension is fully supported. For more information about Unit-of-Order, see Unit-of-Order.
The JMS .NET API does not provide extensions for programmatically configuring JMS resources (for example, topics and queues). In Java, programmatic configuration is accomplished using JMX MBeans or the weblogic.jms.extensions.JMSModuleHelper helper class. Alternative ways to configure JMS include WLST scripting and the WebLogic Administration Console.
Transactions
In this release, the JMS .NET client supports transacted sessions as defined in the JMS Specification only. Transacted sessions provide a standard local transaction capability. As with the Java client, one or more WebLogic JMS destinations from within the same cluster may participate in a transacted session local transaction, but no other resources may participate (such as JMS servers in other clusters, databases, or foreign JMS providers).
Global XA transactions are not supported, therefore JMS cannot participate in a .NET transaction. The XA setting of the connection factory is ignored by the .NET client. The JMS NET client operations cannot participant in any .NET transactions.
Exchanging Messages Between Different Language Environments
The following Java JMS message types can be exchanged between a .NET producer and a Java or C consumer, and vice versa:
- Message
- BytesMessage
- StreamMessage
- MapMessage
- TextMessage
An ObjectMessage type, however, can be sent from one language and received by another, but the message cannot be interpreted unless it is written in the same language. The producer and consumer of an OBJECTMESSAGE type must be written in the same language, either C# or Java. If a mismatch occurs; that is, if a .NET ObjectMessage is received by a Java consumer, or a Java ObjectMessage is received by a .NET consumer, then message.getObject() throws a MessageFormatException.
Specifying the URL Format
The Provider_URL may contain multiple addresses, separated by commas, using the following format:
t3://address [,address]...where a particular address may specify multiple port ranges.
The syntax for specifying multiple addresses is as follows:
- address = hostlist : portlist
where
- hostlist = hostname [, hostname]...
- portlist = portrange [+ portrange]...
- portrange = port [- port]
Use port-port to indicate a port range, and + (plus sign) to separate multiple port ranges.
Table 4-2 provides sample URL formats.
Using DNS Alias Host Names
You can also specify DNS alias host names, which are expanded into multiple hosts. For example, if a DNS alias mycluster resolves to host1,host2, then the URL t3://mycluster:7001 expands into the address list: t3://host1:7001,host2:7001. Contexts that are created with the URL will always retry with host2 if host1 is unreachable. DNS aliases are typically configured by network administrators.
Implementing Security With the JMS .NET Client
You need to be aware of the following security considerations when creating a JMS .NET client:
- To access secure JNDI and JMS resources on the server, the JMS .NET client application can supply a username and password as follows:
- When establishing the initial context to the server using ContextFactory.CreateContext(). The credentials supplied when creating the initial context are used for authentication to gain access to secure JNDI and JMS resources on the server.
- When creating a connection using the IConnectionFactory.createConnection() method. In this case, the credentials supplied when creating a connection override the credentials supplied during the initial context. That is, if user Fred is supplied during initial context, and user Tony is supplied when the connection is created, the user Tony credential is used for authentication to gain access to secure JMS resources.
In both instances, the password is encrypted. If the resources are not secured, a username and password is optional.
WARNING: Although usernames and passwords are protected, and passwords are encrypted, a sophisticated user or intruder might be able to defeat the protection mechanisms. Be sure to secure any network connections when usernames and passwords are provided.
- Authentication for the .NET client is associated with the JMS object that invokes the secured resource. That is, the credential for a JMS object is inherited from the parent JMS context, or from the connection override if credentials are supplied when creating the connection. This differs from Java client security where credentials are associated with the current thread.
- SSL is not supported for the JMS .NET client in this release. Therefore, it is important that you secure the networking services that the operating system provides, as well as any networking connections. For more information, see “Securing Network Connections” in “Ensuring the Security of Your Production Environment” in Securing a Production Environment.
- Similar to the Java client, the JMS.NET client does not support message level encryption.
- Due to the use of non-encrypted communication, sniffing of application traffic (see http://www.owasp.org/index.php/Sniffing_application_traffic_attack) is possible. You need to either accept these risks, or take remediation such as using a firewall to protect against these attacks.
- The administration port, if configured, accepts only SSL traffic, and all connections via the port require administrator privileges. In addition, once an administration port is configured, all other ports will refuse connections that have administrator privileges. Because SSL is not supported for the JMS .NET client in this release, it cannot support users with administrative privileges if an administration port is configured.
Configuring Logging and Debugging
Basic logging and debugging is available for the server-side transport and .NET client host running on WebLogic Server.
Server Side
To enable debugging on the server side, use the following commands:
-Dweblogic.debug.DebugJMSDotNetT3Server=true-Dweblogic.debug.Debug.JMSDotNetProxy=true
Client Side
Client-side logging and debugging are enabled and controlled by various configuration settings in the application configuration file. For generated build files, the application configuration file is named yourapplicationname.exe.config, where yourapplicationname is the name of the application that runs the messaging client.
Listing 4-1 provides the XML content that needs to be added to your application configuration file to configure logging and debugging. The subsequent sections provide additional details about each of the different settings. If you have an existing yourapplicationname.exe.config file, add the XML content shown in the following listing to the file. Otherwise, you can create one and locate it in the same directory that contains the yourapplicationname.exe file.
If you are using Visual Studio, the logging and debugging settings shown in Listing 4-1 need to be added to the App.config file. Follow the instructions on the Microsoft Web site http://msdn.microsoft.com/en-us/library/ms184658.aspx to add an App.config file to your C# project inside a Visual Studio environment. Listing 4-1 XML File Content for yourapplicationname.exe.config File
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!-- To forward log output to a file, please uncomment the following line, and replace the file name with the desired one -->
<!--- <add key="weblogic.JMSDotNet.debugConfig.LogFileName" value="c:\test\MyLogFile.log" /> -->
<!-- To prevent log messages from displaying to the console, use the value 'false' -->
<!-- <add key="weblogic.debug.JMSDotNet.config.IsLogToConsole" value="false" /> -->
</appSettings>
<system.diagnostics>
<switches>
<!-- Please set the switch value as desired for logging to each Category -->
<!-- value for Off=0, Error=1, Warning=2, Info=3, Verbose=4 -->
<!-- if "AllLogger" is enabled (no zero for the value), every individual category is set to the same level as the AllLogger,
no matter how individual category's value is set -->
<add name="weblogic.debug.JMSDotNet.All" value="0" />
<add name="weblogic.debug.JMSDotNet.Socket" value="0" />
<add name="weblogic.debug.JMSDotNet.T3" value="0" />
<add name="weblogic.debug.JMSDotNet.Transport" value="0" />
<add name="weblogic.debug.JMSDotNet.PhysicalMsg" value="0" />
<add name="weblogic.debug.JMSDotNet.LogicalMsg" value="0" />
</switches>
</system.diagnostics>
</configuration>Message Output
Use the <appSettings> element to specify whether log messages are output to the console or saved to a file as shown in Table 4-3.
weblogic.JMSDotNet.debugConfig.IsLogToConsole Boolean
- True — Displays log messages to the console
- False — Does not display log messages to the console
Log Categories and Levels
Client-side logging is grouped into the following categories:
- Socket
- T3
- Transport
- PhysicalMsg
- LogicalMsg
- All (represents all individual categories listed above)
For each of the categories, you can specify any of these logging levels:
Off(0), Error(1), Warning(2), Info(3), Verbose(4)
Note that the severity level on the All category overrides the setting on each individual category.
Understanding Socket and Threading Behavior
WebLogic JMS .NET clients share the same WebLogic Server T3 port as other types of WebLogic clients. When an IContext initial context is created by a .NET client using the ContextFactory class, the client specifies a URL that references a T3 capable port on the server, and a socket pair is implicitly created to service the requested network connection. The socket pair consists of one socket on the client and another socket on the WebLogic Server JMS .NET client host. All JMS operations on JMS objects obtained from the .NET context route through the implicit network connection of the context.
If two concurrent IContext initial context instances on the same .NET CLR connect to the same WebLogic Server JMS .NET client host, then two network connections are created. Each network connection has its own pair of sockets: a server-side socket and a client-side socket. Therefore, when two network connections are created, two sockets are created on the CLR client and two sockets are created on the WebLogic Server acting as the JMS .NET client host. This contrasts with WebLogic Java clients, which automatically detect and close duplicate network connections to a remote JVM and, instead, implicitly multiplex all traffic to and from a particular remote JVM over a single network connection.
A server-side socket for a JMS .NET client is serviced by the same WebLogic Server socket-reader muxer thread pool as other types of WebLogic clients. When working on behalf of JMS .NET client requests, the socket-reader muxer thread pool reads the incoming requests from the socket and dispatches work into the WebLogic Server default thread pool which, in turn, processes the requests and sends the responses back to the client.
On a JMS .NET client, a new internal thread is automatically created for each network connection (that is, per IContext initial context instance). This dedicated thread reads all incoming data on the client socket and dispatches the related work into the CLR thread pool. This means that asynchronous message event handlers in the .NET client application run in the CLR thread pool.
The CLR thread pool is supplied by the .NET Framework System.Threading.ThreadPool class. There is one thread pool per process. The thread pool has a default size of 25 threads per available processor, however, you can change the number of threads in the thread pool using the ThreadPool.SetMaxThreads method. Each thread in the thread pool uses the default stack size and runs at the default priority. For more information, refer to the Microsoft .NET Framework documentation for the System.Threading.ThreadPool class.
For JMS .NET applications that create many concurrent initial contexts that all connect to the same WebLogic Server .NET client host, you may obtain performance improvements by modifying the application so that it uses a single, shared initial context. A shared context ensures that the client only creates a single network connection.
Data Conversion Between Java and .NET
Endian Conversions
Java and .NET use different byte order formats for storing primitive types:
- Microsoft Windows .NET uses the Little-Endian (low-order) format
- Java uses the Big-Endian (high-order) format
To support interoperability between Java and .NET, data is transferred over the network using the Big-Endian format. When a .NET application uses the JMS .NET API to read and write primitives, data is automatically converted between Big-Endian and Little-Endian, as needed. For example, if you use BytesMessage.WriteInt in the JMS .NET API, the data is always stored as Big Endian and can be read using both the Java API and the JMS .NET API bytes message read integer methods.
For specialized applications that do not use the JMS .NET API to pass primitives, but instead transfer primitive data using raw byte arrays, you need to manually convert the byte format to Big Endian when communicating with Java. If you need to perform a manual Endian conversion in your application, you can use the following helper methods from the utility class WebLogic.Messaging.Transport.Util.EndianConvertor provided in the JMS .NET client library:
public static char SwitchEndian(char x)
public static short SwitchEndian(short x)
public static int SwitchEndian(int x)
public static long SwitchEndian(long x)
public static ushort SwitchEndian(ushort x)
public static uint SwitchEndian(uint x)
public static ulong SwitchEndian(ulong x)
public static double SwitchEndian(double x)
public static float SwitchEndian(float x)
public static byte[] SwitchEndian(byte[] x)For example, the standard .NET classes System.IO.BinaryReader and System.IO.BinaryWriter for reading and writing primitives to raw byte arrays use Little Endian. The following code snippet illustrates how to store and retrieve an integer to/from a .NET byte array:
binaryWriter.WriteInt(EndianConverter.SwitchEndian(i))
i=EndianConverter.SwitchEndian(binaryReader.ReadInt())
Signed and Unsigned Byte Conversions
With the exception of the byte data type, there is an equivalent C# data type, with the same name and definition, for every Java primitive data type. The following table lists the different names used for signed and unsigned bytes in C# and Java.
Table 4-4 Byte Primitive Data Type in C# and Java C# Java Description byte N/A Unsigned byte sbyte byte Signed byte
As shown in Table 4-4, Microsoft .NET supports both byte (unsigned byte) and sbyte (signed byte) as primitive data types, but Java supports only byte (signed byte) as a direct primitive type. The standard convention in both languages is to use the byte data type; however, in .NET this represents an unsigned byte and in Java this represents a signed byte.
For interoperability between .NET and Java, the JMS .NET client allows only the use of the signed byte for reading and writing bytes. There is no difference between signed bytes and unsigned bytes when the byte value is 127 or less. An unsigned byte with a value of 127 or less is stored as an sbyte. However, if a .NET client needs to store an unsigned byte with a value greater than 127 in a signed byte, it needs to be converted from a signed byte to an unsigned byte. The following samples illustrate conversion methods that you can use to read and write an unsigned byte as a signed byte:
- Byte Conversion in C#
An unsigned byte value of 255 can be passed as a signed byte as follows:
- byte unsignedByteValue = 255;
sbyte signedByteValue = unchecked ( (sbyte)unsignedByteValue ); // converted signed value=-1Similarly, you can use the following method to convert a signed byte value to an unsigned byte value:
- sbyte signedByteValue = -1;
byte unsignedByteValue = unchecked ( (byte)signedByteValue ); // converted unsigned value=255- Byte Conversion in Java
The unsigned value can be read as a signed byte and converted to an unsigned byte value as follows:
- byte signedByteValue = -1;
int unsignedByteValue = 0xFF & signedByteValue; //converted signed value = 255An unsigned value can be written as follows:
- Int unsignedByteValue = 255;
byte signedByteValue = 0xFF & unsignedByteValue; // converted signed value=-1The JMS .NET API only allows for storing single bytes as signed bytes. When the JMS .NET API is used to retrieve sbyte values as short, int, long, or string, the value is treated as an sbyte, not an unsigned byte. For example, if the unsigned byte value 255 is stored using message.SetByteProperty("myvalue", unchecked( (sbyte)((byte)255) )), a call to message.GetByteProperty("myvalue")or message.GetShortProperty("myvalue") returns "-1".
Byte Array Transfers
When transferring byte arrays from the JMS .NET client to WebLogic JMS, all byte arrays (byte[]) are passed as is (that is, there is no conversion from unsigned to signed.) Therefore, no data is lost in the translation.
Time Conversions
The WebLogic JMS .NET API represents dates and times using Java rather than .NET conventions. The JMSTimestamp and JMSExpiration attributes of the WebLogic.Messaging.IMessage message interface are type long and contain a millisecond absolute time value as specified in the Java programming language. The Java millisecond absolute time value is the difference, measured in milliseconds, between a given time and midnight, January 1, 1970 UTC.
The following examples demonstrate how to convert between .NET times and Java millisecond absolute time values. Listing 4-2 Example C# Code for Converting the Current .NET Time to Java Millisecond Time
// Example: C# code for converting the current .NET time to Java millisecond timeListing 4-3 Example C# Code for Converting Java Millisecond Time to .NET Time
DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0);
DateTime utcNow = DateTime.UtcNow;
long timeInMillis = (utcNow.Ticks - baseTime.Ticks)/10000;
Console.WriteLine(timeInMillis);// Example: C# code for converting Java millisecond time to .NET time
DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0);
long utcTimeTicks = (timeInMillis * 10000) + baseTime.Ticks;
DateTime utcTime = new DateTime(utcTimeTicks, DateTimeKind.Utc);
Console.WriteLine(utcTime);
Console.WriteLine(utcTime.ToLocalTime());
Best Practices
The following list identifies best practices to use when creating a JMS .NET client application:
- Always register a connection exception listener using an IConnection if the application needs to take action when an idle connection fails. The connection exception listener is asynchronously notified when there is a communications failure between the .NET client and the .NET client WebLogic host, or between the WebLogic host and the JMS connection host. Applications may choose to implement the connection exceptions listener callback to close all open resources and then periodically attempt a reconnect.
- To obtain performance improvements, have multiple .NET client threads share a single context to ensure that they use a single socket. For more information, see Understanding Socket and Threading Behavior. It is important to note that a context creates a socket and that closing the context closes the socket.
- Cache and reuse frequently accessed JMS resources, such as contexts, connections, sessions, producers, destinations, and connection factories. Creating and closing these resources consumes significant CPU and network bandwidth.
- With the exception of close() methods, JMS sessions and their child resources are not thread safe. For example, do not call a producer send() in one thread, and a consumer receive() in parallel in another thread, if the producer and consumer were created using the same session. As another example, do not call any method other than close() in an arbitrary thread for sessions that have asynchronous consumers because a message may arrive and invoke the callback at the same time.
- Use DNS aliases or comma separated addresses for load balancing JMS .NET clients across multiple JMS .NET client host servers in a cluster. In this release, the JMS .NET client does not support automatic cluster load balancing as is implicitly supplied with the Java client.