Message sending and reception is a communication mechanism between processes and applications. A process/application may send messages to several clients as well as receive messages from several clients. These messages are sent to message queues. Message queues are containers that host the messages while in transit. Thus, processes and applications get connected to message queues that have mechanisms to send and receive messages. The main purpose of the queue is providing a route and guaranteeing that the messages will be delivered.
Therefore, message queues make it possible for the communication between processes/applications that are not continuously connected. The natural communication process is as follows: a client sends a message to a queue and then the receiver will read the message from the same queue. Nevertheless, the most interesting aspect of this mechanism is that the one who sends the message and the one who receives it does not need to be executing at the same time, which results in the introduction of the concept of asynchronous communication.
In client/server environments, when application A wants to communicate with application B, application A needs to know where application B is located (e.g.: needs to know its IP address) and it also needs application B to be listening at this moment. When using a messages system, application A sends a message, the messages system receives the message and sends it to B, and B will receive the message once it gets connected to the service (the queue). This enables asynchronous communication between A and B; that is to say, B does not need to be present the moment the message is sent, and it will receive the message anyway, in spite of not being present. In fact, the one who sends the message does not need to know anything at all about the one who receives the message and vice versa. The only thing that must be known is the message structure to process it later on.
The Queue data type allows getting connected to a message queue and sending messages to or receiving them from the queue. This data type will, therefore, make it possible to develop applications that require interoperation with other applications that are not connected, with all the advantages of this type of programmings such as asynchronous communication and processing.
The Queue data type will allow you getting connected with a message queue. Once this connection is established, you will be able to send or receive messages. The messages sent are defined by the QueueMessage data type, consisting of a text, a priority (see QueueMessage data type).
The Queue data type enables you to get connected to the two types of existing queues:
- Point to point: a message is sent to the queue and then a client consumes the message (and does not longer exist in the queue). This means that once a message has been sent, a client can then connect to the queue and retrieve the message (if it hasn't been consumed yet).
- Publish / Subscribe: a message is sent to the queue and only the clients that are already connected to the queue will be able to receive the message. If a client connects to the queue after the message has been sent, this client won't receive the message.
Properties
Provider |
Indicates the provider of the queue to be used defined in the jms.xml file. |
User |
Overwrites the user setup in the provider. |
Password |
Overwrites the password setup in the provider. |
Browse |
Indicates whether the messages will be kept in the queue or if they will be removed from it. |
Provider
Indicates the provider of the queue to be used. It is the name of the provider that must have been previously defined in the jms.xml file (see Defining providers). This file must be located in the application directory (DATANNN).
User
Overwrites the user setup in the provider.
&queue.user = "myUser"
Password
Overwrites the password setup in the provider.
&queue.password = "myPassword"
Browse
Indicates whether the messages will be kept in the queue or if they will be removed from it.
0: Messages will be used and removed from the queue.
1: Messages will be used and then they will be kept in the queue.
The default value is 0.
&queue.browse = 1
Connect |
Establishes a connection with the queue defined in the provider. |
Disconnect |
Ends the connection with the queue. |
Send |
Sends a message to the queue. |
Commit |
Commits all the messages that haven't been committed |
Rollback |
Rollback all the messages that haven't been committed |
<numeric> = Connect()
Establishes a connection with the queue defined in the provider. It returns 1 if the connection was possible and 0 if it was not possible.
&num = &queue.connect()
Disconnect
It ends the connection with the queue.
&queue.disconnect()
<String> = Send(QueueMessage message)
Sends a message to the queue. Returns an ID of the message if it was possible to send it; otherwise, it returns "".
&char = &queue.send(&queueMessage)
Commit
Sends all the messages that weren't committed to the queue (if Queue_AutoCommit = NO. See "Defining Providers").
Rollback
Rollback all the messages that haven't been sent to the queue (if Queue_AutoCommit = NO. See "Defining Providers").
Processing messages
To process the messages you must use the for in command as follows:
For &message in &queue
... //Work with &message
EndFor
Defining providers in Java
To define a provider it is necessary to create a jms.xml file under the application directory. This file will contain information about different providers. The following is a complete example of a jms.xml file.
<JMS_Providers>
<Provider>
<Name>queueProvider</Name>
<User></User>
<Password></Password>
<Type>Queue</Type>
<Factory>com.sun.enterprise.naming.SerialInitContextFactory</Factory>
<URL>iiop://myserver:1050</URL>
<JNDI_ID>QueueConnectionFactory</JNDI_ID>
<Queue_Name>Queue</Queue_Name>
<Queue_AutoCommit>YES</Queue_AutoCommit>
</Provider>
<Provider>
....
</Provider>
</JMS_Providers>
<Name> |
Provider's name. This name will then be used in GeneXus when connecting to the queue (&queue.Provider = queueProvider). |
<User> |
User´s identification. |
<Password> |
The password of the user-specified at <User> |
<Type> |
The type can be either a Queue or a Topic depending of the destination defined in the server. |
<Factory> |
This is the initial context factory class which is different for every server. For the J2EE application server, it is com.sun.enterprise.naming.SerialInitContextFactory. For OpenJMS server: org.exolab.jms.jndi.InitialContextFactory. |
<URL> |
Server's Url. |
<JNDI_ID> |
Name of the connection factory previously defined in the server. |
<Queue_Name> |
Name of the queue (the destination previously defined in the server). |
<Queue_AutoCommit> |
Indicates if the messages will have to be committed or not. If "NO" is specified, messages will be stored in memory and it will be necessary to make &queue.commit() in order to send them. If "YES", there is no need to make &queue.commit() because they will be automatically sent every time the &queue.Send() method is invoked. |
See also "Creating queues with J2EE Software Development Kit (SDK) version 1.3.1" for further information.
In .NET the provider must be defined in the client.exe.config file of the application. Under AppSetting it is necessary to add:
<add key="<Queue-ProviderName>" value="<server>\<queue>"/>
Where ProviderName is the name that is going to be set to &queue.provider property, <server> is the name of the server and <queue> is the name of the queue defined in the server. Take into account that when defining the queue, the prefix "Queue" must be included in the definition but it is not used from GeneXus (only the ProviderName is used): E.g.
In client.exe.config:
<add key="Queue-MyFirstQueue" value="paul-xp\myQueue"/>
Then from GX you should use:
&queue.provider = "MyFirstQueue"
In order to define a message queue in .NET you previously must have installed the Messsage Queuing service. After that, you will be able to create a Queue. Take into account that when creating the queue, you can set it as a transactional queue or not. If transactional is set to NO, commit and rollback method of the message queue data type will have no effect and messages will automatically be sent in the "Send" method (so it won't be necessary to commit the messages).
The QueueMessage data type defines the messages that are sent/received once the communication with the message queue is established (See Queue data type).
Properties
Text |
Is the text of the message sent or received. |
Priority |
Indicates the message priority. |
Text
This is the text of the message sent or received.
&message.text = "This is a message"
Priority
Indicates the priority of the message. It may be from 0 to 9.
&message.priority = 7
&queue = Queue data type
&message = QueueMessage data type
&char = Character
1. Provider settings
<JMS_Providers>
<Provider>
<Name>queueProvider</Name>
<User></User>
<Password></Password>
<Type>Queue</Type>
<Factory>com.sun.enterprise.naming.SerialInitContextFactory</Factory>
<URL>iiop://myserver:1050</URL>
<JNDI_ID>QueueConnectionFactory</JNDI_ID>
<Queue_Name>Queue</Queue_Name>
</Provider>
</JMS_Providers>
2. Message sending
&queue.Provider=queueProvider
&ret = &queue.Connect()
&message.Text = "First message"
&message.Priority = 7
&char = &queue.Send(&message)
&message.Text = "Second Message"
&message.Priority = 6
&char = &queue.Send(&message)
3. Message reception.
&queue.Provider = queueProvider
&ret = &queue.Connect()
for &message in &queue
msgbox(&message.text)
endfor
When using queue data type in web environments the most important thing to remark is that it is necessary to connect to the message queue server every time a message is sent or every time we want to get a message. This is due to the fact that in web environments it is not possible to keep the connection to the queue message server alive. That's why, every time someone wants to send or receive a message, they must first get connected to the server. E.g:
Event 'SendMessage'
&queue.Provider=trim(&provider)
&ret = &queue.Connect()
if &ret = 1
&message.Text = &text
&queue.Send(&message)
else
msg("Cannot connect to server")
endif
EndEvent // 'AddMsg'
Examples of usage of this data type may be several and quite varied. As mentioned before, the main motivation to use this data type is asynchronous processing. It is quite common in synchronic programming to have a procedure that calls a specific routine that takes much time and whose result does not matter very much. In these cases, the caller always remains waiting that the called routine finalizes to be able to continue itself. Cases like the previous one or similar ones are examples where the use of message queues would significantly improve productivity making this routine to be processed in another PC or at another time.
So a possible example could be saving "less" important information while the server is busy with other most important processes. Then, this "less" important information that was stored in the message queue will be processed in a batch manner, when the server is less busy.
Nevertheless, the use of message queues would not be suitable in applications requiring to process in real-time.
This is a question that many of you may be asking yourselves, and an evaluation of the matter is worth it. When the DBMS is called, the call is made synchronically, while when you use the message queue the operation is asynchronous. This implies that the use of the message queue has an impact on application performance, making it faster. In case of using a table, the application will have to wait until the server responds.
Another significant aspect is that the table constrains the message information limiting you to the fields that you must send. If you want to send extra information, using a table would not be the ideal choice. On the other hand, the use of messages allows you to add extra information without any limitation.
First, it is necessary to download and install J2EE SDK 1.3.1. It is possible to get it from:
http://java.sun.com/j2ee/sdk_1.3/
One the SDK has been installed there is a tool to perform administrative tasks. This tool is under j2sdkee1.3.1 in and is called j2eeadmin.bat. The tool allows creating Connection Factories and Destinations (queues or topics).
Connection Factories are objects that clients use to create a connection with a provider. By default, there are two connection factories that come preconfigured with the J2EE SDK and are accessible as soon as you start the service: QueueConnectionFactory and TopicConnectionFactory. It is possible to create new connection factories by using the following:
j2eeadmin -addJmsFactory jndi_name queue
j2eeadmin -addJmsFactory jndi_name topic
The jndi_name is the name that should be used then when creating the jms.xml file. E.g.:
j2eeadmin -addJmsFactory myConnectionFactory queue
<JNDI_ID>myConnectionFactory</JNDI_ID> (this is a fragment of jms.xml, see also Defining Providers)
Destinations are objects that clients use to specify the target of messages they produce and the source of messages they consume. It is possible to create new destinations by using the following:
j2eeadmin -addJmsDestination queue_name queuej2eeadmin -addJmsDestination topic_name topic
The queue_name/topic_name is the name that should be used then when creating the jms.xml file. E.g.:
j2eeadmin -addJmsDestination queue
<Queue_Name> myFirstQueue </Queue_Name>
(this is a fragment of jms.xml, see also Defining Providers).
After the Connection Factories and Destinations are created, all will be ready to create the jms.xml file and use the Queue data type from GeneXus.
Configuring OpenJMS
- Download OpenJMS: http://sourceforge.net/projects/openjms
- In order to work with OpenJMS, it is necessary to define a queue and a connector in the server. This is configured in the openjms.xml file which is under the config directory. This sample will use a TCP connector and a queue named queue1. Copy the file tcp_jms.xml from config example to config directory and rename it to openjms.xml. This file already has a TCP connector and a queue ready to be used.
- Start the server. Under openjms in directory execute: openjms run.
Add the following provider to the jms.xml, according to your preferences.
<Provider>
<Name>MyProvider</Name>
<User></User>
<Password></Password>
<Type>Queue</Type>
<Factory>org.exolab.jms.jndi.InitialContextFactory</Factory> //importante
<URL>tcp://localhost:3035</URL> //importante
<JNDI_ID>JmsQueueConnectionFactory</JNDI_ID>
<Queue_Name>queue1</Queue_Name>
</Provider>
Copy the following files to the WEB-INFlib directory of your application:
exolabcore-0.3.7.jar
jms-1.0.2a.jar
jndi-1.2.1.jar
openjms-client-0.7.6.1.jar
Before using queue data type in web applications see the considerations explained in "Using Queue data type in web environments" for further information.
Objects: Procedures, Work Panels, Web Panels, Transactions
Languages: Java, .NET
Interfaces:Web, Win