FILENAME Statement, JMS Access Method

Assigns a fileref that enables you to access a JMS destination by using the JMS access method to send and receive messages of the type TextMessage.
Valid in: Anywhere
Category: Data Access
Requirement: The JAR files for your JMS provider must be included in your CLASSPATH= environment variable.
Tip: For the ActiveMQ JMS provider, you must either specify the TIMEOUT=option or configure your jms.prefetchPolicy.queuePrefetch property to 0.

Syntax

FILENAME fileref JMS <"destination-name" > <access-method-options>

Summary of Optional Arguments

specifies the Java Naming and Directory Interface (JNDI) name for the JMS destination.
specifies a metadata authentication domain that is associated with user credentials.
specifies the JNDI name of the JMS connection factory.
specifies a list of options that are added to the environment that is used to create the initial JNDI context.
specifies the correlation ID for outgoing messages, or stores the correlation ID for incoming messages to a variable.
specifies the JMS delivery mode.
specifies the JNDI name for the JMS destination.
specifies a string that indicates the end of the file.
specifies the fully qualified class name of the JNDI provider initial context factory.
specifies server information that is used to create the JNDI initial context.
specifies the record length in bytes.
adds the correlation ID, the message ID, or both to your message selector string.
assigns message ID values to the DATA step variable that you specify.
specifies a filter string that restricts the messages that are received by the access method.
specifies the password that is used to connect to the JMS provider.
specifies the message priority for outgoing messages, where a larger priority number indicates a higher priority.
specifies a time limit in milliseconds to wait for a message to arrive.
specifies the default length of time in milliseconds that a message is retained by the message system.
specifies the user ID that is used to connect to the JMS provider.

Required Arguments

fileref
is a valid file reference.
JMS
specifies the access method that enables you to access Java Messaging Service (JMS) destinations.

Optional Argument

"destination-name"
specifies the Java Naming and Directory Interface (JNDI) name for the JMS destination.
Interaction:You can specify a destination by using either the destination-name argument, the DESTINATION= option, or aggregate syntax.

Access Method Options

AUTHDOMAIN= "domain"
specifies a metadata authentication domain that is associated with user credentials. The credentials from the authentication domain are used to connect to the JMS provider.
Tip:The value does not require quotation marks if it is a valid SAS name.
CONNFACTORY= "JNDI-connection-name"
specifies the JNDI name of the JMS connection factory. The connection factory contains a set of configuration parameters that are used to connect to the JMS provider.
Alias:CONNFAC=
Tip:The value does not require quotation marks if it is a valid SAS name.
CONNOPTIONS= "options-list"
specifies a list of options that are added to the environment that is used to create the initial JNDI context. Specify the options as space-delimited name-value pairs. For example,
CONNOPTIONS="option1=value option2=value"
Aliases:CONNECTIONOPTS=

CONNOPTS=

CORRELATIONID=correlId-variable
specifies the correlation ID for outgoing messages, or stores the correlation ID for incoming messages to a variable. For the FILE statement, specifies a DATA step variable whose value is used as the correlation ID of the message.
For the INFILE statement:
  • the correlation ID of the incoming message is stored in the variable that you specify.
  • the value of the variable that you specify is used as the correlation ID for message selector when you specify MATCHOPTS=CORRELID or MATCHOPTS=BOTHIDS.
Note: The CORRELATIONID= option is valid in the FILE and INFILE statements only.
Alias:CORRELID=
DELIVERYMODE=PERSISTENT | NONPERSISTENT
specifies the JMS delivery mode. Specify one of the following:
PERSISTENT
specifies that messages are stored in a stable storage location. Messages can be recovered if the JMS provider fails to deliver the message.
NONPERSISTENT
specifies that messages are not stored in a stable storage location. The NONPERSISTENT mode offers better performance than the PERSISTENT mode, but messages might be lost if the JMS provider fails.
Default:PERSISTENT
DESTINATION= "destination-name"
specifies the JNDI name for the JMS destination.
Alias:DEST=
Interaction:You can specify a destination by using either the destination-name argument, the DESTINATION= option, or aggregate syntax.
Tip:The value does not require quotation marks if it is a valid SAS name.
EOFCHECK= "check-string"
specifies a string that indicates the end of the file. The JMS access method returns end of file when it encounters a message that matches the EOFCHECK= value or the EOFCHECK= value prefixed with the string "OBJMQ_STOP:"
For example, if you specify EOFCHECK="STOPMSG", then a message value of either STOPMSG or OBJMQ_STOP:STOPMSG causes the JMS access method to return end of file.
JNDIICTXTFACTORY="factory-class-name"
specifies the fully qualified class name of the JNDI provider initial context factory. The class that you specify is used to create the JNDI initial context and provides the starting point for the resolution of names..
Alias:JNDIICTXTFAC=
JNDIPROVIDERURL="JNDI-provider-URL"
specifies server information that is used to create the JNDI initial context. The server information that you specify has the basic format of a URL string. However, the specific formatting of the URL string depends on the class that you use to create the JNDI initial context. See the class documentation for your initial context class for details about how to specify the provider URL.
Alias:JNDIPROVURL=
LRECL= n|nk
specifies the record length in bytes. Add k to the number to specify a multiple of 1024.
Default:256
MATCHOPTIONS= CORRELATIONID | MESSAGEID | BOTHIDS
adds the correlation ID, the message ID, or both to your message selector string. specify one of the following values:
CORRELATIONID
adds the correlation ID to the selector string.
Alias:CORRELID
MESSAGEID
adds the message ID to the selector string.
Alias:MSGID
BOTHIDS
adds both the correlation ID and the message ID to the selector string.
Alias:MATCHOPTS=
MESSAGEID= variable
assigns message ID values to the DATA step variable that you specify. You can use the MESSAGEID= option to obtain the message ID of an incoming or outgoing message. For outgoing messages, each Write operation writes a new message ID value to the variable.
Note: The MESSAGEID= option is valid in the FILE and INFILE statements only.
Alias:MSGID=
MSGSELECTOR= "filter-string"
specifies a filter string that restricts the messages that are received by the access method. The filter string uses SQL92 conditional expression syntax. A message selector matches a message if the selector expression evaluates to true for that message.
For example, if you specify msgselector="JMSPriority = 9", then the access method only receives messages where the priority is 9.
PASSWORD= "password" | _PROMPT_
specifies the password that is used to connect to the JMS provider. Specify a password or specify _PROMPT_ to be prompted to enter your password interactively.
Note: You can submit a password as clear text or as an encoded string from the PWENCODE procedure. For more information, see Encryption in SAS 9.3.
Aliases:PWD=

PW=

Tips:You can specify the AUTHDOMAIN= option instead of the USER= and PASSWORD= options.

The value does not require quotation marks if it is a valid SAS name.

PRIORITY=n
specifies the message priority for outgoing messages, where a larger priority number indicates a higher priority. The value must be between 0 and 9.
PRIORITY= has no effect for incoming messages. You can filter incoming messages based on priority by using the value of JMSPriority in a message selector. For example, the following message selector specifies a priority of 5.
msgselector="JMSPriority=5"
Default:4
TIMEOUT=n
specifies a time limit in milliseconds to wait for a message to arrive. If the time expires before a message arrives, then the access method returns end of file. A value of –1 indicates that the access method returns end of file immediately if there are no messages. A value of 0 indicates that the access method will wait an unlimited amount of time for a message to arrive.
Note: For the ActiveMQ JMS provider, if your TIMEOUT= value is –1, then the provider will not poll the message broker unless your jms.prefetchPolicy.queuePrefetch property is set to 0. For details about setting your prefetch property for ActiveMQ, see ActiveMQ documentation.
Default:-1
TIMETOLIVE=n
specifies the default length of time in milliseconds that a message is retained by the message system.
Default:0 (no limit)
USER= "user-ID" | _PROMPT_
specifies the user ID that is used to connect to the JMS provider. Specify a user ID or specify _PROMPT_ to be prompted to enter your user ID interactively.
Alias:USERID=
Tips:You can specify the AUTHDOMAIN= option instead of the USER= and PASSWORD= options.

The value does not require quotation marks if it is a valid SAS name.

Details

The SAS JMS file access method allows SAS programs to read and write records to and from any message-oriented middleware that supports a JMS provider. The SAS JMS file access method starts a Java Virtual Machine (JVM) if the SAS session has not already done so. The access method uses the Java Native Interface (JNI) to interact with the JMS provider in the JVM.
You can use the JMS file access method to send and receive JMS messages of the type TextMessage. By using SAS formats and informats, you can send and receive character data and numeric data through the JMS access method as long as the data can be represented as text.
Filerefs assigned with the JMS file access method can be used in the same ways as other external file access methods can be used, including with the following statements: FILENAME, FILE, INFILE, and %INCLUDE. The CORRELATIONID= and MESSAGEID= options are valid only in the FILE and INFILE statements. If you specify a fileref on both the FILENAME statement and the FILE or INFILE statement, then the option values from the FILE or INFILE statement will override the values from the FILENAME statement.
If your JMS provider requires native libraries, then use the JREOPTIONS= system option to add the native library path to your -Djava.library.path property.
Note: The JMS file access method uses the MessageConsumer interface to receive messages from the JMS destination. This means that the messages that you receive with the JMS access method are removed from the JMS destination.
Note: In order to use the JMS access method, you must specify the JAR files for your JMS provider in your CLASSPATH= environment variable. For more information, see the documentation for your JMS provider. On z/OS, add the JAR files to the TKJNI_OPT_CLASSPATH record in the TKMVSENV data set. For more information, see the SAS Companion for z/OS.
For more information about JMS, see the following resources:
  • the JMS Tutorial at download.oracle.com/javaee/1.3/jms/tutorial/index.html
  • the Java Message Service specification at www.oracle.com/technetwork/java/jms/index.html
  • the JMS API at download.oracle.com/javaee/6/api/javax/jms/package-summary.html

Examples

Example 1: Basic Send and Receive Using WebSphere MQ

This example shows a simple send and receive using WebSphere MQ as the JMS provider. The first DATA step puts a message on a queue, and the second DATA step receives the message and writes it to the SAS log. This example assumes that the JMS administrator created administrative objects for a connection factory named myqmgr and a destination named myq, and that the JNDI name space is located at \\mynode\public\jndi.
filename myref jms 'myq'
    jndiictxtfactory="com.sun.jndi.fscontext.RefFSContextFactory"
    jndiproviderurl="file:////mynode/public/jndi"
    connfac=myqmgr;

data _null_;
    file myref;
    put 'This is a test message';
run;

data _null_;
    infile myref;
    input mymsg $22.;
    put mymsg=;
run;

Example 2: Using ActiveMQ

This example shows a simple send and receive using ActiveMQ as the JMS provider. In this example, the FILE and INFILE assign filerefs rather than using a FILENAME statement. Note that you can install Active MQ by using Installing and Testing ActiveMQ from a SAS Program.
No administrative tasks are required for this example. The CONNOPTS= option specifies a name-value pair that is used by the ActiveMQ provider to create the administrative object for the myq destination. The TIMEOUT= option causes the underlying receive call to wait up to 100 milliseconds for a message to arrive.
data _null_;
    file 'myq' device=jms
        jndiiCtxtFactory='org.apache.activemq.jndi.ActiveMQInitialContextFactory'
        jndiProviderURL='tcp://localhost:61616'
        connfactory='ConnectionFactory'
        connopts='queue.myq=myq';
    put 'This is a test message';
run;

data _null_;
    infile 'myq' device=jms timeout=100
        jndiiCtxtFactory='org.apache.activemq.jndi.ActiveMQInitialContextFactory'
        jndiProviderURL='tcp://localhost:61616'
        connfactory='ConnectionFactory'
        connopts='queue.myq=myq';
    input mymsg $22.;
    put mymsg=;
run;

Example 3: Using WebSphere Application Server

This example fragment assigns a fileref using WebSphere Application Server as the JMS provider.
filename myref jms 'MyJMSClientQueue'
    jndiictxtfactory='com.ibm.websphere.naming.WsnInitialContextFactory'
    jndiproviderurl='iiop://mynode:9816'
    connfactory='MyJMSQueueConnectionFactory';

Example 4: Using JBoss Application Server

This example fragment assigns a fileref using JBoss Application Server as the JMS provider.
filename myref jms 'myorg/jms/myq'
    jndiictxtfactory='org.jnp.interfaces.NamingContextFactory'
    jndiproviderurl='jnp://mynode:1099'
    connfactory='/ConnectionFactory';

Example 5: Using WebLogic Application Server

This example fragment assigns a fileref using WebLogic Application Server as the JMS provider.
filename myref jms 'myorg/jms/myq'
    jndiiCtxtFactory='weblogic.jndi.WLInitialContextFactory'
    jndiProviderURL='t3://mynode:7001'
    connfactory='weblogic.jms.ConnectionFactory';

Example 6: Installing and Testing ActiveMQ from a SAS Program

This example downloads and installs ActiveMQ and then performs a simple send and receive test using the local ActiveMQ installation.

Prerequisite

In order to use ActiveMQ, you must add the JAR files that are required by your JMS provider to your Java CLASSPATH. You can use the —SET CLASSPATH option on your SAS start-up command to add the JAR files.
For this example, your CLASSPATH should include install-Dir/apache-activemq-5.6.0/activemq-all-5.6.0.jar, where install-Dir is the value of the installDir= macro variable in the example code.
For example, using the default installDir= value on Windows, add the following to your SAS command:
-set classpath "c:\apache-activemq-5.6.0\activemq-all-5.6.0.jar"

Alternative Download Method

You can also download ActiveMQ manually from activemq.apache.org.
Specify installation parameters.The tempZipFile= macro variable specifies the filename for the downloaded installation file. The installDir= macro variable specifies the directory where ActiveMQ will be installed (ActiveMQ will be installed in a subdirectory named apache-activemq-5.6.0.)
%let tempZipFile=c:\\activemq.zip;
%let installDir=c:\\;
Download the ActiveMQ binary distribution from the Web.If necessary, specify the proxy for your Internet connection by using the PROXY= option in the FILENAME statement. If you encounter a connection error, check the ActiveMQ download site to obtain a mirror URL.
filename fromweb URL
'http://www.us.apache.org/dist/activemq/apache-activemq/5.6.0/apache-activemq-5.6.0-bin.zip'
    lrecl=8192 recfm=s;

data _null_;
    infile fromweb;
    file "&tempZipFile" recfm=n lrecl=8192;
    input;
    put _infile_;
run;
Extract the ActiveMQ zip file into the destination directory.
proc groovy;
    add sasjar="ANT";
    submit "&tempZipFile" "&installDir";
        def ant = new AntBuilder();
        ant.unzip( src:args[0], dest:args[1], overwrite:"false" )
        ant.delete( file:args[0] )
    endsubmit;
quit;
Assign a fileref associated with a third-party message queue.The JNDIPROVIDERURL= option value specifies a connection to the embedded ActiveMQ broker through the VM protocol.
filename myq jms 'dynamicQueues/myqueue'
    jndiiCtxtFactory='org.apache.activemq.jndi.ActiveMQInitialContextFactory'
    jndiProviderURL='vm://broker'
    connfactory='ConnectionFactory'
    timeout=100;
Put some test messages on the queue.
data _null_;
    file myq;
    format a datetime21.1;
    a = datetime(); put a ' Testing. 1 2';
    a = datetime(); put a +1 'This is a test. 3 4';
    a = datetime();
    put a @22 'This is only a test. 5 6';
run;
Get the messages back off of the queue and print them in the log.
data _null_;
    infile myq length=len;
    input a datetime21.2 @;
    len = len - 25;
    input b $varying60. len c d;
    put a= datetime19. b= c= d=;
run;

Example 7: Using the XML LIBNAME Engine

This example demonstrates using the XML LIBNAME engine to write and read data from a queue in XML format.

Details

This example uses an XML map file to store metadata for the data set. You can create an XML map file by using the SAS XML Mapper. For more information about XML map files and the XML LIBNAME engine, see the SAS XML LIBNAME Engine: User's Guide.
Create a sample data set.
data pointofsale;
    infile cards;
    input saleId date e8601dt22.2 amount customerId storeId $4.;
    format date e8601dt22.2 amount dollar6.2;
    cards;
0001 2009-09-15T15:51:00.51 11.91 1001 A001
0002 2009-09-15T15:52:00.52 12.92 1002 B002
0003 2009-09-15T15:53:00.53 13.93 1003 A003
0004 2009-09-15T15:54:00.54 13.94 1004 A004
0005 2009-09-15T15:55:00.55 13.95 1005 A005
0006 2009-09-15T15:56:00.56 13.96 1006 A006
0007 2009-09-15T15:57:00.57 13.97 1007 A007
0008 2009-09-15T15:58:00.58 13.98 1008 A008
0009 2009-09-15T15:59:00.59 13.99 1009 A009
0010 2009-09-15T15:53:01.00 14.00 1010 A010
0011 2009-09-15T15:53:01.01 14.01 1011 A011
;
run;
Assign a fileref to a dynamic queue named "myqueue".This example code uses the same ActiveMQ provider as Installing and Testing ActiveMQ from a SAS Program, but you can use any JMS provider.
filename mymq JMS 'dynamicQueues/myqueue'
    jndiCtxtFactory='org.apache.activemq.jndi.ActiveMQInitialContextFactory'
    jndiProviderURL='vm://broker'
    connFactory='ConnectionFactory'
    timeout=100;
Assign a libref that uses the XML engine and JMS file access method.
libname mymq xmlv2;
Put the data set on the queue in XML format.
data mymq.pointofsale;
    set work.pointofsale;
run;
Create an XML map for the data set.The XML map for this example is created through a DATA step for demonstration purposes. For your own data sets, use the SAS XML Mapper to create XML map files.
filename myXMLmap temp;
data _null_;
    file myXMLmap;
    infile cards;
    input;
    put _infile_;
    cards;

<?xml version="1.0" encoding="UTF-8"?>
<SXLEMAP name="AUTO_GEN" version="2.1">
    <NAMESPACES count="0"/>
    <TABLE description="POINTOFSALE" name="POINTOFSALE">
        <TABLE-PATH syntax="XPath">/TABLE/POINTOFSALE</TABLE-PATH>
        <COLUMN name="saleId">
            <PATH syntax="XPath">/TABLE/POINTOFSALE/saleId</PATH>
            <TYPE>numeric</TYPE>
            <DATATYPE>integer</DATATYPE>
        </COLUMN>
        <COLUMN name="date">
            <PATH syntax="XPath">/TABLE/POINTOFSALE/date</PATH>
            <TYPE>numeric</TYPE>
            <DATATYPE>double</DATATYPE>
        </COLUMN>
        <COLUMN name="amount">
            <PATH syntax="XPath">/TABLE/POINTOFSALE/amount</PATH>
            <TYPE>numeric</TYPE>
            <DATATYPE>double</DATATYPE>
        </COLUMN>
        <COLUMN name="customerId">
            <PATH syntax="XPath">/TABLE/POINTOFSALE/customerId</PATH>
            <TYPE>numeric</TYPE>
            <DATATYPE>integer</DATATYPE>
        </COLUMN>
        <COLUMN name="storeId">
            <PATH syntax="XPath">/TABLE/POINTOFSALE/storeId</PATH>
            <TYPE>character</TYPE>
            <DATATYPE>string</DATATYPE>
            <LENGTH>4</LENGTH>
        </COLUMN>
    </TABLE>
</SXLEMAP>
;
run;
Assign a libref that uses the XML engine, the XML map file, and the JMS access method.
libname mymq xmlv2 xmlmap=myXMLmap;
Read XML off the queue and create a data set.
data myds;
    set mymq.PointOfSale;
    put _all_;
run;
Print the data set.
proc print data=myds;
    format date e8601dt22.2 amount dollar6.2;
run;

Example 8: Using Request and Response Queues

This example uses separate request and response queues to send messages and then retrieve them by using their message ID.

Details

The MESSAGEID= and CORRELID= options are used to store the message ID values. The MATCHOPTS= option is used to generate a message selector that retrieves the messages based on their ID values.
Although this example runs in a single process, a common request and response scenario uses many asynchronous processes to create requests.
Assign a fileref for the message queue.
filename x jms
    jndiiCtxtFactory='com.sun.jndi.fscontext.RefFSContextFactory'
    jndiProviderURL='file://localhost/jndi'
    connfactory=MyFactory;
Put some data onto a request queue.The MESSAGEID= option in the FILE statement stores message IDs for each message in the mID variable.
data requests;
    length name $40;
    file x(myRequest) messageid=mID deliverymode=nonpersistent;
    loanAmount=1000;   creditScore=675; name='Brown, Bob';
    put loanAmount @10 creditScore @15  name;
    output;
    loanAmount=2000;   creditScore=500; name='Wilson, Jane';
    put loanAmount @10 creditScore @15  name;
    output;
    loanAmount=3000;   creditScore=750; name='White, William';
    put loanAmount @10 creditScore @15  name;
    output;
    loanAmount=50000; creditScore=700;  name='Jones, Fred';
    put loanAmount @10 creditScore @15  name;
    output;
    stop;
run;
Retrieve data from the request queue, process the data, and send it to the response queue.The MESSAGEID= option in the INFILE statement reads the message IDs from the request queue into the mID variable. The CORRELID= option in the FILE statement stores the request queue message IDs as correlation IDs for the response queue.
data _null_;
    length name $40 mID $51;
    infile x(myRequest) messageid=mID length=len;
    file x(myResponse) correlid=mID deliverymode=nonpersistent;
    input loanAmount creditScore @;
	len = len - 14;
    input +1 name $varying40. len;
    if ( creditScore > 600 and loanAmount < 10000 ) then
       decision = 'approved';
    else decision = 'declined';
    put decision name;
run;
Sort the data set to demonstrate that the messages are read according to the message ID and not simply in sequence.
proc sort data=requests; by name; run;
Read the messages from the response queue and match the correlation IDs to the message IDs.The MATCHOPTS= option in the INFILE statement specifies that the correlation ID is used to select the messages from the queue. The CORRELID= option in the INFILE statement specifies that cID variable contains the correlation ID values.
data responses;
    length msgName $40 cID $51;
    drop msgName mID cID len;
    set requests;
	cID=mID;
    infile x(myResponse) matchopts=correlid correlid=cID length=len;
    input @1 answer $8. @;
	len = len - 9; 
Compare the customer names to verify that the data from the request queue and the response queue are a match.
    input +1 msgName $varying40. len;
	if ( name ^= msgName ) then put 'Error:  match failed.';
run;
Print the data set.
proc print data=responses;
run;