Since Version 9.2, Weblogic Server has this neat feature called the Store-and-Forward (SAF) client, which enables a JMS remote client to keep messages locally whenever a connection problem occurs with the server. When the connection is re-established, the messages are delivered.
From a developer's viewpoint, this behavior is almost transparent—our code will complete the procedure without any errors, as if the message were actually delivered to its destination queue. This is a great feature to use when network outages are frequent, or even when you only have a specific time window to communicate with the server—instead of dealing with all the batching details from inside your code, you just delegate this responsibility to the JMS transport.
Also, the changes you have to apply to your code in order to enable the SAF client are pretty simple. The following is a typical set of parameters that we must declare in order to acquire a server connection and use a JMS queue:
Context.INITIAL_CONTEXT_FACTORY=weblogic.jndi.WLInitialContextFactory Context.SECURITY_PRINCIPAL=weblogic Context.SECURITY_CREDENTIALS=welcome1 Context.PROVIDER_URL=t3://localhost:7001 QUEUE_CONNECTION_FACTORY=jms/yourQueueConnectionFactory QUEUE_NAME=jms/yourQueue
To enable the JMS SAF client, we basically use the same set of parameters by dropping one and changing the other three:
Context.INITIAL_CONTEXT_FACTORY= weblogic.jms.safclient.jndi.InitialContextFactoryImpl Context.SECURITY_CREDENTIALS=packt Context.PROVIDER_URL=file:/opt/packt/etc/SAFClient.xml QUEUE_CONNECTION_FACTORY=jms/yourQueueConnectionFactory QUEUE_NAME=jms/yourQueue
The following is a description of the changes:
PROVIDER_URL
parameter must point to a configuration file generated by a command-line utility, ClientSAFGenerate
. We're going to set up this file just after this block.INITIAL_CONTEXT_FACTORY
parameter must point to a class named weblogic.jms.safclient.jndi.InitialContextFactoryImpl
that knows how to parse the file provided by Context.PROVIDER_URL
.SECURITY_PRINCIPAL
parameter declares the username necessary to establish a connection to the server. When using the SAF client, though, this information is inside the XML file referred by PROVIDER_URL
, so we don't need to use it when acquiring the server connection.SECURITY_CREDENTIALS
parameter usually holds the user's password that will open the connection. When using the SAF client, this credential is also put inside the XML file, but we still need this entry—it will hold a password key that the engine must use to decrypt the password inside the configuration file.We don't need to change anything else in the code other than these set of parameters, so the portion of code that deals with acquiring a handle to the JMS queue and posts a message to it remains unchanged.
When using the SAF client, there's one caveat that you must be aware of: the messages you post will always reach the local repository first, and then they will be delivered to the appropriate server. By default, the SAF client holds the message for 20 seconds before trying to send it to the server.
So, if your client isn't designed to stay in memory, for instance, listening to a TCP port for incoming messages, you must tweak this configuration to avoid having all messages kept at the local store. We will see how to do it shortly.
One final observation: the SAF client doesn't participate in distributed transactions (XA). If your design has this kind of requirement, you can either use the SAF client knowing that it runs local transactions ( it doesn't influence any XA transactions that may be in course) or not use it at all.
The following is the sequence of steps we must execute in order to enable the SAF client:
Let's do it.
Almost all the information that the SAF client needs to connect to a server is located inside a configuration file created by the ClientSAFGenerate
utility. Here's how we create it:
setDomainEnv.sh
(on Windows, setDomainEnv.cmd
), to set up the environment—you can find it inside the bin
folder of your domain:$ cd $DOMAIN_HOME/tickets/bin
$ source ./setDomainEnv.sh
/config/jms
inside the domain—WebLogic Server creates one file for each JMS system module that you declare and saves them inside the subfolder of your domain:$ cd ../config/jms
$ ls –w1
jmsmodule-tickets-jms.xml
$ java weblogic.jms.extensions.ClientSAFGenerate -url t3://localhost:7001 -username weblogic -moduleFile jmsmodule-tickets-jms.xml -outputFile /opt/packt/etc/SAFClient.xml
Leave the terminal open, we will use it again later. The utility doesn't connect to the server to gather information, as the URL and username parameters could imply—they are used to create the appropriate entries in the configuration file—so take extra care when typing them.
If everything went well, the /opt/packt/etc/SAFClient.xml
file has been created with a reference to our destination queue and information about the connection:
<saf-imported-destinations name="jmsmodule-tickets"> <saf-queue name="ExhibitionQueue"> <remote-jndi-name>jms.tickets.exhibition</remote-jndi-name> <local-jndi-name>jms.tickets.exhibition</local-jndi-name> </saf-queue> <saf-remote-context>RemoteContext0</saf-remote-context> </saf-imported-destinations> <saf-remote-context name="RemoteContext0"> <saf-login-context> <loginURL>t3://localhost:7001</loginURL> <username>weblogic</username> </saf-login-context> </saf-remote-context> </weblogic-client-jms>
We must encrypt the connection password inside the configuration file, but the utility that generates it doesn't accept a password parameter, so we must create it using another application:
java -Dweblogic.management.allowPasswordEcho=true weblogic.jms.extensions.ClientSAFEncrypt
packt
—and hit Enter.welcome1
if you're following the book standard—and hit Enter.<password-encrypted>{Algorithm}AES/CBC/PKCS5Padding{Salt}OMDCZlaTWng={IV}KCcjtoVJqxYeQXvmKukpmg=={Data}Sm1TYOAlERODdHKHqKvGwaNU/YZuJXIhn/THV9+yel8=</password-encrypted>
quit
to exit the utility. As we have just one value, go ahead and finish it.There are three things we must add to the configuration file, SAFClient.xml
, so open it with a text editor and proceed with the following changes:
password-encrypted
generated tag just below the username
tag:<saf-remote-context name="RemoteContext0">
<saf-login-context>
<loginURL>t3://localhost:7001</loginURL>
<username>weblogic</username>
<password-encrypted>{Algorithm} … </password-encrypted>
</saf-login-context>
</saf-remote-context>
saf-imported-destinations
tag:<connection-factory name="wls.default"> <jndi-name>weblogic.jms.ConnectionFactory</jndi-name> <transaction-params> <xa-connection-factory-enabled>false </xa-connection-factory-enabled> </transaction-params> </connection-factory>
0
, so as soon as a message is sent from our code, it will try to deliver the message to the server. To do it, add this block of tags after the connection-factory
block we just inserted:<saf-agent> <default-retry-delay-base>0</default-retry-delay-base> </saf-agent>
We just have to change a few lines of code of our Enqueue
class to be able to use the SAF client feature.
As said earlier, we need to change three connection parameters: provider_url
, the user password, A.K.A security credentials
, and the JNDI connection factory. The following are the current relevant lines of code:
static final String WLS_ADDRESS =
"t3://localhost:7001";
static final String WLS_CTX_FACTORY =
"weblogic.jndi.WLInitialContextFactory";
env.put(Context.SECURITY_CREDENTIALS, "welcome1");
They must be changed to something similar to the following code:
// Points to the configuration file
static final String WLS_ADDRESS =
"file:/opt/packt/etc/SAFClient.xml";
// Must have this exact value – it's the SAF Context Factory
static final String WLS_CTX_FACTORY =
"weblogic.jms.safclient.jndi.InitialContextFactoryImpl";
// The password you use to encrypt the login password
env.put(Context.SECURITY_CREDENTIALS, "packt");
Also, we can comment the line that attaches a user to the context, as this information is already in the configuration file:
// env.put(Context.SECURITY_PRINCIPAL, "weblogic");
We now have to add the SAF T3 client library to the project's classpath:
wlthint3client.jar
library, select and click on wlsaft3client.jar
, and then click on Enter.And we're good to go.
This is the easiest part, just run the remote client a couple of times!
When the application is started, you will see a group of messages in the console stating that the SAF client has been initialized:
<Thu Dec 13 12:05:56 BRST 2012> <Info> <Store> <WL-280008> <Opening the persistent file store "SAFSTORE0V" for recovery: directory=/opt/packt/etc/stores/default requestedWritePolicy="Direct-Write" fileLockingEnabled=true driver="NIO".>
<Thu Dec 13 12:05:57 BRST 2012> <Info> <Store> <WL-280009> <The persistent file store "SAFSTORE0V" (91256ed7-83b7-4b9d-a9c5-61831fe5bb74) has been opened: blockSize=512 actualWritePolicy="Direct-Write(single-handle-buffered)" explicitIOEnforced=false records=11.>
<Thu Dec 13 12:05:58 BRST 2012> <Info> <Messaging> <WL-282003> <The messaging kernel ClientSAFAgent0 will use up to 318,155,434 bytes of memory before paging begins.>
<Thu Dec 13 12:05:58 BRST 2012> <Info> <Messaging> <WL-282001> <The messaging kernel ClientSAFAgent0 is beginning recovery of its persistent state.>
<Thu Dec 13 12:05:58 BRST 2012> <Info> <Messaging> <WL-282002> <The messaging kernel ClientSAFAgent0 has recovered 0 persistent messages.>
<Thu Dec 13 12:05:58 BRST 2012> <Info> <Messaging> <WL-282003> <The messaging kernel ClientSAFAgent0 will use up to 318,155,434 bytes of memory before paging begins.>
Then, after a few seconds, another message will be printed stating that the client acquired a connection to the server:
Agent "ClientSAFAgent0" got connected to RemoteContext0 while processing messages for ExhibitionQueue
To test the feature, you can stop WebLogic Server and post a few messages, then start it and run the remote client one more time. When you do this, the client will print recurrent error messages, but that's fine. Notice that one of the console messages prints the number of JMS messages being held by the SAF client:
<WL-282002> <The messaging kernel ClientSAFAgent0 has recovered 4 persistent messages.>
The following are few web resources that you can refer to:
weblogic.jms.extensions