Cab Python 3 Read From Jms Queue

9. JMS Messaging¶

Coffee Message Service has been a well known means for decoupling the Java application's parts or to provide an integration service for otherwise asunder Coffee applications. Cheers to JMS being purely an API, Spring Python offers a fashion for connecting to JMS providers and to participate in JMS messaging scenarios. JMS messages sent and received by a Spring Python powered application are no different than messages produced and consumed past Coffee applications, in fact, yous can apply Leap Python and JMS with no Java applications participating in message exchanging at all.

Jump Python works as a JMS client, you notwithstanding need a JMS provider, the server part, for bulletin brokering. The just JMS provider currently supported by Jump Python is IBM's WebSphere MQ, formerly known as MQSeries.

Although Spring Python's JMS API is loosely based on Spring Java's, it'due south not a direct port and features a highly Pythonic expect and feel.

_images/jms-map.png

Note

Througout the chapter pure Python code or YAML syntax is used to illustrate the support for JMS however it only represents the author'south preferences and it's worth noting that you tin utilize any of Spring Python's formats to configure the IoC container. Or you can apply no IoC at all as it'due south a completely optional feature and one that'due south not strictly required past JMS.

ix.ane. Introduction¶

JMS messaging with Jump Python revolves around the idea of using a connection factory for obtaining a connexion to a JMS provider and springpython.jms.core.JmsTemplate as a ways for sending and receiving messages. A JmsTemplate instance is tied to a connexion mill however a single connectedness factory may be safely reused across multiple JmsTemplates.

In addition to that, springpython.jms.listener.SimpleMessageListenerContainer allows for a purely configuration-driven way to prepare background JMS listeners to receive messages from JMS providers.

9.2. Dependencies¶

Support for JMS messaging with WebSphere MQ is built on top of the CPython-simply PyMQI library which provides Python applications an access to WebSphere MQ queue managers. Yous demand to separately install PyMQI in order to apply springpython.jms.factory.WebSphereMQConnectionFactory. PyMQI, in turn, needs a WebSphere MQ client, a runtime library which may be freely downloaded from IBM'due south site.

springpython.jms.listener.SimpleMessageListenerContainer, a Jump Python component which helps with running background JMS listeners, requires the installation of Circuits 1.2+ and threadpool 1.two.7 or newer.

9.3. Quick first¶

Here'southward a few quick examples that volition become you started with Jump Python and JMS. Both Python code and IoC with YAML syntax are shown. Information technology's causeless there's a QM.1 queue director running on host 192.168.1.121 with its listener on port 1434 and connections are made through the server connection channel SVRCONN.1 to queues TEST.1 and Test.ii.

9.3.1. Sending¶

First, allow'due south transport a message using nix but pure Python code:

                from                springpython.jms.core                import                JmsTemplate                from                springpython.jms.mill                import                WebSphereMQConnectionFactory                qm_name                =                "QM.1"                channel                =                "SVRCONN1.1"                host                =                "192.168.ane.121"                listener_port                =                "1434"                queue1                =                "TEST.i"                # The connection manufacturing plant we're going to use.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                aqueduct                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connexion factory for really communicating with a JMS provider.                jms_template                =                JmsTemplate                (                mill                )                # And that's it, now we put the mandatory "Hello world" message on a queue.                jms_template                .                send                (                "Hello earth"                ,                queue1                )                # Nosotros're not using an IoC so we must close down the connection factory ourselves.                factory                .                destroy                ()              

Now do the same but use an IoC container configured via springpython.config.YamlConfig. The configuration should be saved in a "jms-context.yml" file in the same directory the Python lawmaking using it will exist saved in:

                objects                :                -                object                :                MyConnectionFactory                course                :                springpython.jms.factory.WebSphereMQConnectionFactory                properties                :                queue_manager                :                QM.1                channel                :                SVRCONN.1                host                :                192.168.i.121                listener_port                :                "1434"                -                object                :                MyTemplate                course                :                springpython.jms.core.JmsTemplate                properties                :                manufactory                :                {                ref                :                MyConnectionFactory                }                -                object                :                MyQueue                str                :                Test.1              

And the Python code using the above IoC configuration:

                from                springpython.context                import                ApplicationContext                from                springpython.config                import                YamlConfig                container                =                ApplicationContext                (                YamlConfig                (                "./jms-context.yml"                ))                # Read the objects definitions from configuration.                queue1                =                container                .                get_object                (                "MyQueue"                )                jms_template                =                container                .                get_object                (                "MyTemplate"                )                # Send the message.                jms_template                .                send                (                "Hullo world"                ,                queue1                )                # The connection factory is now being managed by the IoC container which takes                # intendance of shutting downwards the mill. No need for manually destroying it.              

An obvious alter is that the configuration is now kept separately from the implementation but some other advantage is that the container will close down the connectedness factory on itself as springpython.jms.factory.WebSphereMQConnectionFactory is a bracket of springpython.context.DisposableObject which means its .destroy method will be executed when the container volition be shutting downward.

9.3.two. Receiving¶

The very aforementioned connexion manufactory and JmsTemplate can be used for both sending and receiving. Examples below employ the aforementioned definitions of objects as the sending examples do, they are repeated here for the sake of completness:

                from                springpython.jms.core                import                JmsTemplate                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.ane"                aqueduct                =                "SVRCONN.1"                host                =                "192.168.i.121"                listener_port                =                "1434"                queue1                =                "TEST.ane"                # The connexion factory nosotros're going to use.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                aqueduct                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connection factory for actually communicating with a JMS provider.                jms_template                =                JmsTemplate                (                manufactory                )                # Get a message off the queue. The telephone call to receive will by default time out                # subsequently 1000ms and enhance springpython.jms.NoMessageAvailableException then.                jms_template                .                receive                (                queue1                )                # Nosotros're not using an IoC and then we need to close downwards the connection factory ourselves.                factory                .                destroy                ()              

And here's a complementary case showing the usage of YamlConfig. The configuration should be saved in a "jms-context.yml" file in the same directory the Python code using it will be saved in. Note that it'southward the same configuration that was used in the sending example:

                objects                :                -                object                :                MyConnectionFactory                class                :                springpython.jms.manufacturing plant.WebSphereMQConnectionFactory                properties                :                queue_manager                :                QM.1                channel                :                SVRCONN.one                host                :                192.168.one.121                listener_port                :                "1434"                -                object                :                MyTemplate                form                :                springpython.jms.core.JmsTemplate                properties                :                manufactory                :                {                ref                :                MyConnectionFactory                }                -                object                :                MyQueue                str                :                Exam.1              

The Python code used for receiving a message from a queue configured using the YamlConfig:

                from                springpython.context                import                ApplicationContext                from                springpython.config                import                YamlConfig                container                =                ApplicationContext                (                YamlConfig                (                "./jms-context.yml"                ))                # Read the objects definitions from configuration                queue1                =                container                .                get_object                (                "MyQueue"                )                jms_template                =                container                .                get_object                (                "MyTemplate"                )                # Get a message off the queue. The telephone call to receive volition by default fourth dimension out                # after 1000ms and raise springpython.jms.NoMessageAvailableException then.                jms_template                .                receive                (                queue1                )                # The connection factory is now beingness managed past the IoC container which takes                # intendance of shutting downwards the factory. No need for manually destroying it.              

Hither'southward a sample YAML context utilizing the SimpleMessageListenerContainer component and an accompanying Python code using it. As y'all tin can see, a mere fact of providing the configuration allows for receiving the letters:

                objects                :                -                object                :                connection_factory                class                :                springpython.jms.factory.WebSphereMQConnectionFactory                properties                :                queue_manager                :                QM.1                aqueduct                :                SVRCONN.one                host                :                192.168.i.121                listener_port                :                "1434"                -                object                :                message_handler                grade                :                app.MyMessageHandler                -                object                :                listener_container                course                :                springpython.jms.listener.SimpleMessageListenerContainer                properties                :                manufactory                :                {                ref                :                connection_factory                }                handler                :                {                ref                :                message_handler                }                destination                :                Exam.1              
                # app.py                from                springpython.config                import                YamlConfig                from                springpython.context                import                ApplicationContext                form                MyMessageHandler                (                object                ):                def                handle                (                self                ,                bulletin                ):                impress                "Got message!"                ,                message                if                __name__                ==                "__main__"                :                # Obtaining a context will automatically start the SimpleMessageListenerContainer and its listeners in background.                container                =                ApplicationContext                (                YamlConfig                (                "./context.yml"                ))                while                Truthful                :                # Hither goes the application's logic. Whatsoever JMS letters, as configured                # in ./context.yml, will exist passed in to a singleton MyMessageHandler instance.                pass              

9.iv. Connection factories¶

9.4.ane. WebSphereMQConnectionFactory¶

springpython.jms.factory.WebSphereMQConnectionFactory implements access to WebSphere MQ JMS provider. Forth with JmsTemplate and SimpleMessageListenerContainer it's the form you lot'll be well-nigh oftentimes using for sending and receiving of messages.

Each WebSphereMQConnectionFactory object volition concord at nigh one connection to WebSphere MQ, which will be lazily established when it'll exist actually needed, e.grand. when a message will demand to exist put on a queue for the kickoff fourth dimension. The connection will always exist started in WebSphere MQ's customer style, there'southward no back up for connecting in the bindings mode.

Like all Jump Python'due south classes WebSphereMQConnectionFactory tin be configured using pure Python or y'all can use Spring Python's IoC to divide your business lawmaking from configuration. Using IoC has an added do good of taking care of destroying any open up queues and closing the connection when the IoC shuts down - we'll get to it in a moment.

WebSphereMQConnectionFactory provides several options that let y'all customize its behaviour and apart from the obvious ones which you lot must provide (similar, the queue managing director's host) all other options take sensible defaults which y'all'll rarely need to modify, if at all.

Here's a full initializer method reproduced for convenience and the explanation of default values used:

def __init__(self, queue_manager=None, channel=None, host=None, listener_port=None,         cache_open_send_queues=True, cache_open_receive_queues=Truthful,         use_shared_connections=Truthful, dynamic_queue_template="System.DEFAULT.MODEL.QUEUE",         ssl=False, ssl_cipher_spec=None, ssl_key_repository=None):
queue_manager

default: None

Must be ready manually

Proper name of the queue managing director, e.thousand. EAI.QM.one

aqueduct

default: None

Must be set manually

Name of a server connection (SVRCONN) channel through which the connection volition be established, e.g. EAI.SVRCONN.1

host

default: None

Must exist set up manually

Host name or IP on which the queue manager is running, e.g. 192.168.1.103

listener_port

default: None

Must be set manually

Port on which the queue managing director'south listener is accepting TCP connections, due east.m. 1434

cache_open_send_queues

default: True

Past default, WebSphereMQConnectionFactory will go on references to open up queues in a enshroud for after re-use. This speeds-upward near operations every bit there's usually no need for endmost a queue if information technology'southward going to be used in subsequent calls to queue manager. At times withal, it's prefered to shut the queues as shortly equally possible and cache_open_send_queues controls whether queues open for putting the messages on are to be kept in the cache.

cache_open_receive_queues

default: True

This setting controls whether queues open up for receving of messages should be kept in a cache. If set up to False, they will be airtight after the call to get a message off the queue will accept finished.

use_shared_connections

default: True

A single WebSphereMQConnectionFactory object may be shared between multiple threads to provide better performance. This setting allows for marking the underlying connexion to a queue manager as a not-shareable and makes sure that only ane thread will be able to use it, any call to the factory from a thread that didn't open the connection will result in a springpython.jms.JMSException being raised. The setting should only set to False when connecting to queue managers running on z/OS systems equally it otherwise can injure the functioning of multi-threaded applications. It has no impact on functioning of single-threaded applications.

dynamic_queue_template

default: SYSTEM.DEFAULT.MODEL.QUEUE

The name of a model queue basing on which the dynamic queues will be created. It is commonly desirable to override the default value equally, unless customized, Organisation.DEFAULT.MODEL.QUEUE is a not-shared (NOSHARE in MQ speak) queue and doesn't allow for opening the dynamic queues for both sending and receiving.

ssl

default: False

A boolean value which indicates whether connections to the queue manager should utilize a customer SSL/TLS document. ssl_cipher_spec and ssl_key_repository must also exist provided if ssl is True.

ssl_cipher_spec

default: None

An SSL/TLS naught spec to apply for encrypted connections, its value must be equal to that of the MQ SVRCONN channel's SSLCIPH aspect.

ssl_key_repository

default: None

On-disk location of an SSL/TLS client certificates repository. The repository must be of type CMS, such a repository can be created using gsk6cmd/gsk7cmd control line tools. Note that the value of this attribute must not contain a suffix; for instance, if there are following files in /var/mqm/security: client-repo.crl, client-repo.kdb, customer-repo.rdb and client-repo.sth, then ssl_key_repository must be ready to "/var/mqm/security/client-repo".

Here'southward an example of programatically creating a WebSphereMQConnectionFactory object:

                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.1"                channel                =                "SVRCONN.1"                host                =                "192.168.1.121"                listener_port                =                "1434"                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                channel                ,                host                ,                listener_port                )                # ... employ factory here.                # E'er destroy the mill when not using an IoC container.                manufacturing plant                .                destroy                ()              

An instance of using YamlConfig for configuring WebSphereMQConnectionFactory inside of an IoC container:

                objects                :                -                object                :                MyConnectionFactory                class                :                springpython.jms.factory.WebSphereMQConnectionFactory                backdrop                :                queue_manager                :                QM.1                channel                :                SVRCONN.1                host                :                192.168.1.121                listener_port                :                "1434"              

All cached queues volition not be closed by a factory until after its .destroy will have been called which will happen automatically if y'all're using an IoC container. If the manufacturing plant is configured programatically in Python yous must call .destroy yourself in your code. A call to .destroy too closes the factory's connection to a queue manager.

WebSphereMQConnectionFactory objects are thread-rubber and may be shared between multiple threads if the queue managing director supports sharing a single connexion which is the case on all platforms except for z/Bone.

Note

For the curious i

springpython.jms.factory.WebSphereMQConnectionFactory and springpython.jms.factory.MQRFH2JMS wrap the WebSphere MQ's native MQRFH2 wire-level format in a set of Python classes and hibernate any intricate details of communicating with queue managers. From the programmer'south viewpoint, MQRFH2JMS is irrelevant, however it might exist of interest to anyone willing to amend or expand Spring Python'due south JMS support.

9.5. JmsTemplate¶

springpython.jms.cadre.JmsTemplate is the course to use for sending JMS messages; along with SimpleMessageListenerContainer it may likewise be used in order to receive them. A template must exist associated with a connection mill and once configured, may be used for communicating in both directions. It's up to you to determine whether in your circumstances information technology makes sense to reuse a single template for all communications, to have a single template for each queue involved or perhaps to use separate, dedicated, templates, 1 for sending and one for receiving. Annotation nonetheless that JmsTemplate instances are not guaranteed to exist thread-safe and no attempt has been made to make them exist so.

Recollect that factories postpone connecting to a queue director and creating a JmsTemplate instance doesn't necessarily mean in that location volition be no connection errors when it will be first time used for sending or receiving.

Here's how a JmsTemplate may be instantiated using Python code:

              from              springpython.jms.core              import              JmsTemplate              from              springpython.jms.manufacturing plant              import              WebSphereMQConnectionFactory              qm_name              =              "QM.one"              channel              =              "SVRCONN1.1"              host              =              "192.168.1.121"              listener_port              =              "1434"              mill              =              WebSphereMQConnectionFactory              (              qm_name              ,              channel              ,              host              ,              listener_port              )              jms_template              =              JmsTemplate              (              manufactory              )              # E'er destroy the factory when non using IoC              factory              .              destroy              ()            

An example of using YamlConfig to configure a JmsTemplate:

              objects              :              -              object              :              MyConnectionFactory              form              :              springpython.jms.factory.WebSphereMQConnectionFactory              properties              :              queue_manager              :              QM.1              channel              :              SVRCONN.1              host              :              192.168.1.121              listener_port              :              "1434"              -              object              :              jms_template              class              :              springpython.jms.core.JmsTemplate              properties              :              factory              :              {              MyConnectionFactory              }            

JmsTemplate allows for a number of options to customize its behaviour. The only options required to set up manually is the manufactory parameter. Except for factory, all the parameters may be overriden past private calls to sending or receiving of messages:

def __init__(self, factory=None, delivery_persistent=None,         priority=None, time_to_live=None, message_converter=None,         default_destination=None):
factory

default: None

Must exist set up manually

A JMS connection factory associated with this JmsTemplate.

delivery_persistent

default: None

Tells whether messages sent to a JMS provider are by default persistent. If not set, the persistency of messages is controlled on a per letters footing (and defaults to a persistent delivery).

priority

default: None

Messages sent to the provider may exist of different priority, usually on a calibration from 1 to 9. The setting controls the default priority of all messages sent by this JmsTemplate, unless overridden by individual letters. A JMS provider will set the default priority if no value is given here or when sending the individual letters.

time_to_live

default: None

JMS allows for expiry of messages subsequently a certain fourth dimension expressed in milliseconds. The time to live of a message may be gear up here and it volition be applied to all messages sent or tin be set per each bulletin sent. If no value is provided here and when sending the bulletin to a destination, the message expiry time is left to the discretion of a JMS provider.

message_converter

default: None

It is sometimes desirable to not have to deal with raw messages taken from or sent to JMS provider from within a JmsTemplate object, information technology may brand more than sense to delegate converting the objects from and to JMS representation to an external helper class. A message converter is an object that helps decoupling the domain objects from the fact that JMS is the transportation layer used for communicating. A single converter may exist used for converting the incoming every bit well equally outgoing letters. See the section on message converters for more than details and code examples. Setting the bulletin converter here volition take precedence over setting it on a per-bulletin basis.

default_destination

default: None

Information technology is sometimes desirable to not accept to bargain with raw messages taken from or sent to JMS provider from within a JmsTemplate object, it may make more than sense to delegate converting the objects from and to JMS representation to an external helper class. A bulletin converter is an object that helps decoupling the domain objects from the fact that JMS is the transportation layer used for communicating. A single converter may exist used for converting the incoming as well equally outgoing letters. See the section on bulletin converters for more details and lawmaking examples. Setting the message converter here will take precedence over setting it on a per-message footing.

ix.5.1. Sending¶

The basic approach is to transport ASCII strings or unicode objects, which must allow for encoding into UTF-8:

                # -*- coding: utf-8 -*-                from                springpython.jms.core                import                JmsTemplate                from                springpython.jms.mill                import                WebSphereMQConnectionFactory                qm_name                =                "QM.ane"                channel                =                "SVRCONN.one"                host                =                "192.168.1.121"                listener_port                =                "1434"                queue1                =                "Test.ane"                # The connection factory nosotros're going to use.                manufacturing plant                =                WebSphereMQConnectionFactory                (                qm_name                ,                channel                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connectedness factory for really communicating with a JMS provider.                jms_template                =                JmsTemplate                (                manufactory                )                jms_template                .                default_destination                =                queue1                # Send some ASCII                jms_template                .                send                (                "Hello, Spring Python here"                )                # Send unicode                jms_template                .                ship                (                u"Cześć, z tej strony Spring Python"                )                # We're not using an IoC so nosotros demand to shut down the connexion mill ourselves.                factory                .                destroy                ()              

Notation that in an case above the bulletin's destination has been taken from JmsTemplate. Nosotros tin can also specify information technology on send time or we can combine both approaches, similar here:

                # -*- coding: utf-8 -*-                from                springpython.jms.core                import                JmsTemplate                from                springpython.jms.manufactory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.ane"                channel                =                "SVRCONN.1"                host                =                "192.168.1.121"                listener_port                =                "1434"                queue1                =                "TEST.one"                queue2                =                "Examination.ii"                # The connection manufacturing plant we're going to employ.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                aqueduct                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connection mill for actually communicating with a JMS provider.                jms_template                =                JmsTemplate                (                factory                )                jms_template                .                default_destination                =                queue1                # Send some ASCII to one queue                jms_template                .                transport                (                "Hi, Spring Python here"                )                # Send unicode to another queue                jms_template                .                send                (                u"Cześć, z tej strony Spring Python"                ,                queue2                )                # Nosotros're not using an IoC and then nosotros need to shut down the connection factory ourselves.                mill                .                destroy                ()              

Sending is not limited to strings or unicode objects though. You lot can customize a lot of bulletin's properties by sending a springpython.jms.cadre.TextMessage instead. The following example shows how a custom message ID and respond to destination can exist specified for an outgoing message:

                # stdlib                from                uuid                import                uuid4                # Jump Python                from                springpython.jms.core                import                JmsTemplate                ,                TextMessage                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.ane"                channel                =                "SVRCONN.i"                host                =                "192.168.i.121"                listener_port                =                "1434"                queue1                =                "TEST.1"                # The connexion factory we're going to use.                mill                =                WebSphereMQConnectionFactory                (                qm_name                ,                aqueduct                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connection factory for actually communicating with a JMS provider.                jms_template                =                JmsTemplate                (                factory                )                jms_template                .                default_destination                =                queue1                # Generate the correlation ID                jms_correlation_id                =                uuid4                ()                .                hex                message                =                TextMessage                (                "Hullo, Bound Python here"                )                message                .                jms_correlation_id                =                jms_correlation_id                message                .                jms_reply_to                =                "REPLY.TO.QUEUE"                # Transport the message                jms_template                .                transport                (                bulletin                )                # We're non using an IoC so nosotros need to shut down the connection factory ourselves.                factory                .                destroy                ()              

Using TextMessage instances instead of apparently strings or unicode objects is also recommended when you lot're interested in values a JMS provider has given to JMS backdrop of a message after the bulletin had been sent. Here you can see the values which were assigned automatically by the provider to jms_timestamp and jms_message_id properties:

                from                springpython.jms.core                import                JmsTemplate                ,                TextMessage                from                springpython.jms.mill                import                WebSphereMQConnectionFactory                qm_name                =                "QM.1"                channel                =                "SVRCONN.1"                host                =                "192.168.1.121"                listener_port                =                "1434"                queue1                =                "Examination.ane"                # The connection factory we're going to use.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                aqueduct                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connection factory for actually communicating with a JMS provider.                jms_template                =                JmsTemplate                (                manufactory                )                jms_template                .                default_destination                =                queue1                # Create a TextMessage case.                bulletin                =                TextMessage                (                "Howdy, Spring Python hither"                )                # Send the message                jms_template                .                send                (                bulletin                )                impress                "jms_timestamp =                                %south                "                %                bulletin                .                jms_timestamp                impress                "jms_message_id =                                %s                "                %                message                .                jms_message_id                # We're non using an IoC so nosotros need to shut downwardly the connection factory ourselves.                factory                .                destroy                ()                #                # Shows the following here:                #                # $ python jms_properties_overriding.py                # jms_timestamp = 1255885622380                # jms_message_id = ID:414d5120514d2e312020202020202020283cdb4a02220020                # $              

Take a look here for more information virtually how to use TextMessages.

ix.5.ii. Receiving¶

The same JmsTemplate instance may exist used for both sending and receiving of messages. When you receive messages you may optionally provide a timeout value in milliseconds later exceeding which a springpython.jms.NoMessageAvailableException will be raised if no message will have been available for a given JMS destination. Default timeout is k milliseconds.

JmsTemplate may use a default JMS destination for each call to .receive or you tin can explicitly specify the destination's proper noun when you receive letters:

                from                springpython.jms.core                import                JmsTemplate                ,                TextMessage                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.1"                channel                =                "SVRCONN.i"                host                =                "192.168.one.121"                listener_port                =                "1434"                queue1                =                "TEST.i"                queue2                =                "TEST.2"                # The connexion factory we're going to use.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                channel                ,                host                ,                listener_port                )                # Every JmsTemplate uses a connection factory for really communicating with a JMS provider.                jms_template                =                JmsTemplate                (                factory                )                jms_template                .                default_destination                =                queue1                # Send a message to the start queue which is a default destination ..                jms_template                .                send                (                "Hi there!"                )                # .. and now receive it.                print                jms_template                .                receive                ()                # Now send a message to the second ane ..                jms_template                .                transport                (                "Hi there again!"                ,                queue2                )                # .. and at present receive it ..                print                jms_template                .                receive                (                queue2                )                # .. try to receive a message again, this fourth dimension requesting a timeout of ii seconds.                impress                jms_template                .                receive                (                queue2                ,                2000                )                # We're not using an IoC and then nosotros demand to shut down the connectedness manufactory ourselves.                factory                .                destroy                ()              

Note that SimpleMessageListenerContainer provides a complementary way for receiving the messages, peculiarly well suited for long-running processes, such as servers.

9.five.3. Dynamic queues¶

A dynamic queue is a unremarkably short-lived object created on-need by JMS applications, near often found in request-respond scenarios when in that location's no need for the response to be persistently stored. An awarding initiating the communication will create a dynamic temporary queue, send the request to the other side providing the proper noun of the dynamic queue as a destination for the responses to be sent to and look for a certain corporeality of time. With Jump Python and WebSphere MQ, the requesting side must then explicitly close the dynamic queue regardless of whether the response will be received or if the asking timeouts.

The post-obit case shows two JmsTemplate objects communicating via a dynamic queue and imitating an substitution of messages between 2 dispersed applications. You lot can detect than from the responding application's point of view a dynamic queue's name is similar whatsoever other queue'due south name, the application doesn't need to be - and indeed isn't - enlightened that it's responding to a dynamic queue and not to a predefined i. For the requesting end a dynamic queue is also like a regular queue in that its name must be provided to the JmsTemplate's .receive method. Note that WebSphere MQ allows but non-persistent messages to be put on temporary dynamic queues which are the kind of dynamic queues y'all get by default with Leap Python:

                from                springpython.jms                import                DELIVERY_MODE_NON_PERSISTENT                from                springpython.jms.core                import                JmsTemplate                ,                TextMessage                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                qm_name                =                "QM.i"                channel                =                "SVRCONN.1"                host                =                "192.168.1.121"                listener_port                =                "1434"                exchange_queue                =                "TEST.1"                # The connection factory we're going to use.                manufactory                =                WebSphereMQConnectionFactory                (                qm_name                ,                channel                ,                host                ,                listener_port                )                requesting_side                =                JmsTemplate                (                factory                )                requesting_side                .                default_destination                =                exchange_queue                responding_side                =                JmsTemplate                (                manufacturing plant                )                responding_side                .                default_destination                =                exchange_queue                # Create a dynamic queue.                dyn_queue_name                =                requesting_side                .                open_dynamic_queue                ()                # Note that we wrap the whole chat in a try/finally block as we must                # always shut a WebSphere MQ dynamic queue.                try                :                # Create a request message.                message                =                TextMessage                (                "Hey, what'due south up on the other side?"                )                # WebSphere MQ messages sent to dynamic temporary queues must not                # be persistent.                bulletin                .                jms_delivery_mode                =                DELIVERY_MODE_NON_PERSISTENT                # Tell the other side where to send responses.                message                .                jms_reply_to                =                dyn_queue_name                # Send the request                requesting_side                .                transport                (                message                )                # Receive the request ..                request                =                responding_side                .                receive                ()                # .. prepare the response ..                response                =                TextMessage                (                "A bit stormy today!"                )                response                .                jms_delivery_mode                =                DELIVERY_MODE_NON_PERSISTENT                # .. and send our response to a jms_reply_to destination which as we know                # is a dynamic queue in this instance.                responding_side                .                send                (                response                ,                request                .                jms_reply_to                )                # Receive the response. It's being read as usual, as from any other queue,                # there'southward no special JmsTemplate's method for getting letters                # off dynamic queues.                print                requesting_side                .                receive                (                dyn_queue_name                )                finally                :                requesting_side                .                close_dynamic_queue                (                dyn_queue_name                )                # We're non using an IoC so we need to shut downward the connection factory ourselves.                manufacturing plant                .                destroy                ()              

It's worth mentioning again that y'all must close WebSphere MQ dynamic queues yourself as Bound Python won't do that for yous - it'due south a slight departure from how Coffee JMS works.

9.five.iv. Message converters¶

It's quite possible that you'll like to separate the lawmaking responsible for core JMS advice with outside systems from the logic needed for converting your business domain's objects dorsum and forth to strings needed for passing into JmsTemplate'southward methods. Yous may use your own converting classes for it or you lot can use the Spring Python'due south converters for such a work. A converter is a subclass of springpython.jms.core.MessageConverter which must implement at least one of the to_message or from_message methods. There'southward naught magical well-nigh MessageConverter objects and they won't exercise any automatic convertions for you, they're merely interfaces y'all tin can implement every bit you lot'll probable demand some sort of separation between the objects you deal with and the JMS API.

There'southward one deviation you must take into account when using bulletin converters - you don't utilise the standard transport and receive methods but defended convert_and_send and receive_and_convert ones. Other than that, the JMS API and features are exactly the same.

The code below shows a sample usage of MessageConverters. Note that you don't need to implement both to_message and from_message if that's not advisable in your situation notwithstanding it makes sense for the instance below to handle requests and responses using only ane converter object:

                from                springpython.jms.factory                import                WebSphereMQConnectionFactory                from                springpython.jms.core                import                JmsTemplate                ,                MessageConverter                ,                TextMessage                qm_name                =                "QM.1"                aqueduct                =                "SVRCONN.1"                host                =                "192.168.one.121"                listener_port                =                "1434"                # Note that it'south the same queue then we're going to later receive the same invoice nosotros sent.                request_queue                =                response_queue                =                "TEST.1"                # One of the concern domain's objects our application deals with.                course                Invoice                (                object                ):                def                __init__                (                cocky                ,                customer_account_id                =                None                ,                month                =                None                ,                corporeality                =                None                ):                self                .                customer_account_id                =                customer_account_id                self                .                month                =                month                self                .                amount                =                amount                def                __str__                (                cocky                ):                return                "<                %s                                  at                                %south                , customer_account_id=                %south                , month=                %s                , amount=                %due south                >"                %                (                cocky                .                __class__                .                __name__                ,                hex                (                id                (                self                )),                cocky                .                customer_account_id                ,                self                .                month                ,                self                .                corporeality                )                # Let's imagine the other side of a JMS link wants to receive and send CSV data.                class                InvoiceConverter                (                MessageConverter                ):                def                to_message                (                self                ,                invoice                ):                """ Converts a business object to CSV.                                  """                text                =                ";"                .                join                ((                invoice                .                customer_account_id                ,                invoice                .                calendar month                ,                invoice                .                amount                ))                return                TextMessage                (                text                )                def                from_message                (                self                ,                message                ):                """ Produces a business concern object out of CSV data.                                  """                customer_account_id                ,                month                ,                amount                =                message                .                text                .                split                (                ";"                )                invoice                =                Invoice                ()                invoice                .                customer_account_id                =                customer_account_id                invoice                .                month                =                month                invoice                .                amount                =                corporeality                render                invoice                # The connexion manufactory we're going to utilise.                factory                =                WebSphereMQConnectionFactory                (                qm_name                ,                channel                ,                host                ,                listener_port                )                # Our JmsTemplate.                jms_template                =                JmsTemplate                (                factory                )                # Hither we tell the template to use our converter.                invoice_converter                =                InvoiceConverter                ()                jms_template                .                message_converter                =                invoice_converter                # See how we're now dealing just with business objects at the JmsTemplate level.                invoice                =                Invoice                (                "00033010118"                ,                "200909"                ,                "136.32"                )                jms_template                .                convert_and_send                (                invoice                ,                request_queue                )                print                jms_template                .                receive_and_convert                (                response_queue                )                # We're not using an IoC so we demand to shut down the connection mill ourselves.                factory                .                destroy                ()              

9.6. SimpleMessageListenerContainer and background JMS listeners¶

springpython.jms.listener.SimpleMessageListenerContainer is a configuration-driven component which is used to receive messages from JMS destinations. Once configured, the container starts as many groundwork listeners every bit requested and each listener gets assigned a pool of threads to handle the incoming requests. The number of listeners started and threads in a pool is fixed upon the configuration is read and the container is started, they cannot be dynamically altered in runtime.

The reward of using SimpleMessageListenerContainer comes from the fact that all you need to do in order to receive the messages is to create your own handler class and to configure the container, no JMS coding is required so you're focusing on creating the business organisation logic, not on the JMS average.

manufacturing plant

default: None

Must be set manually

A reference to a JMS connection factory.

destination

default: None

Must be set up manually

Proper noun of a JMS destination to read the messages off.

handler

default: None

Must be prepare manually

A reference to an object which will be receiving messages read from the JMS destination. A handler must implement handle(cocky, message) method, of which the bulletin argument is a TextMessage instance. There is a convenience form, springpython.jms.listener.MessageHandler, which exposes such a method. The exact number of handlers bachelor for message processing is controlled via the handlers_per_listener property.

manufacturing plant

default: ane

Sets a number of background processes that connect to a JMS provider and read letters off the destination.

handlers_per_listener

default: 2

Sets a number of background processes that connect to a JMS provider and read messages off the destination.

wait_interval

default: thousand (1 second)

A value in milliseconds expressing how often each of the listeners will check for the inflow of a new message.

Here'southward an example showing SimpleMessageListenerContainer in activity together with YamlConfig's abstruse objects definitions. customer_queue, credit_account_queue and deposit_account_queue subclass the listener_container object which holds the information mutual to all definitions of JMS destinations. 4 listeners will exist assigned to each of the JMS destination, every listener will be assigned a puddle of 5 threads for handling the letters read; a expect interval of 700 milliseconds has been gear up:

              objects              :              -              object              :              connection_factory              class              :              springpython.jms.factory.WebSphereMQConnectionFactory              backdrop              :              queue_manager              :              QM.ane              channel              :              SVRCONN.one              host              :              192.168.ane.121              listener_port              :              "1434"              -              object              :              message_handler              class              :              app.MyMessageHandler              -              object              :              listener_container              abstruse              :              True              class              :              springpython.jms.listener.SimpleMessageListenerContainer              concurrent_listeners              :              "4"              handlers_per_listener              :              "5"              wait_interval              :              "700"              properties              :              factory              :              {              ref              :              connection_factory              }              handler              :              {              ref              :              message_handler              }              -              object              :              customer_queue              parent              :              listener_container              properties              :              destination              :              CUST.QUEUE.1              -              object              :              credit_account_queue              parent              :              listener_container              properties              :              destination              :              CREDACCT.QUEUE.1              -              object              :              deposit_account_queue              parent              :              listener_container              properties              :              destination              :              DEPACCT.QUEUE.ane            

Here'southward a Python code using the above IoC configuration. Note that the fact of reading a configuration alone suffices for JMS listeners to be started and run in the background of the main awarding:

              # app.py              from              springpython.config              import              YamlConfig              from              springpython.context              import              ApplicationContext              class              MyMessageHandler              (              object              ):              def              handle              (              self              ,              message              ):              print              "Got message!"              ,              message              if              __name__              ==              "__main__"              :              # Obtaining a context will automatically start the SimpleMessageListenerContainer              # and its listeners in background.              container              =              ApplicationContext              (              YamlConfig              (              "./context.yml"              ))              while              True              :              # Here goes the principal application's logic, which does zero in this case.              # However, the listeners have been already started and incoming messages              # will be passed in to MyMessageHandler instance (as configured in YamlConfig).              pass            

9.7. TextMessage¶

springpython.jms.core.TextMessage objects encapsulate the data existence sent to or received from a JMS provider. Even if you use the plain jms_template.send("Foobar") to transport an ordinary text, there's all the same a TextMessage instance created automatically underneath for you.

If all you need from JMS is simply to send and receive some text and so you're not probable to exist required to apply TextMessages. All the same, if y'all have to set or read JMS attributes or y'all're interested in setting custom JMS properties then TextMessage is what you're looking for.

In Spring Python in that location are no impuissant setters and getters as in Coffee JMS. If you lot need to set up the property of a message, you just write it, like for example message.jms_correlation_id = "1234567". Hither's the list of all TextMessage's attributes along with their explanation and usage notes.

text

The message contents, the actual business payload carried by a message. May exist both read and written to. For messages sent to a JMS provider it must be either a cord or a unicode object encodable into UTF-8.

The following two lawmaking snippets are equivalent:

                      message                      =                      TextMessage                      (                      "Hey"                      )                    
                      message                      =                      TextMessage                      ()                      message                      .                      text                      =                      "Hey"                    

Here'due south how to get the content of a message received by a JmsTemplate:

                      # .. skip creating the connexion manufacturing plant and a JmsTemplate                      bulletin                      =                      jms_template                      .                      receive                      ()                      print                      message                      .                      text                    
jms_correlation_id

Equivalent to Java's JMSCorrelationID message header. It must exist a string instance when set manually - a good way to produce correlation identifiers is to use the Python's uuid4 type, e.one thousand.:

                      # stdlib                      from                      uuid                      import                      uuid4                      # Spring Python                      from                      springpython.jms.core                      import                      TextMessage                      # Prapare the JMS correlation ID                      jms_correlation_id                      =                      uuid4                      ()                      .                      hex                      message                      =                      TextMessage                      (                      "How-do-you-do"                      )                      message                      .                      jms_correlation_id                      =                      jms_correlation_id                      # Now the message will be sent with a JMS correlation ID such as                      # 6f5b070bb0ed472bbe63d511776bb1dc which is a 128 $.25 long ID.                    
jms_delivery_mode Equivalent to Java's JMSDeliveryMode, tin can be both read and written to and must exist equal to 1 of the post-obit values springpython.jms.DELIVERY_MODE_NON_PERSISTENT, springpython.jms.DELIVERY_MODE_PERSISTENT or springpython.jms.DEFAULT_DELIVERY_MODE. The default value - DEFAULT_DELIVERY_MODE- equals to DELIVERY_MODE_PERSISTENT.
jms_destination Equivalent to Java'southward JMSDestination, automatically populated by JmsTemplate objects on send or receive fourth dimension. May be read from merely must not be gear up manually.
jms_expiration

Same equally Coffee's JMSExpiration - allow for a message to expire afterward a certain amount of time. The value is automatically ready by JmsTemplate for received messages. For letters being sent the time expressed is in milliseconds, as in the following code:

                      bulletin                      =                      TextMessage                      (                      "I will expire in half a second"                      )                      # Set the bulletin'south expiration fourth dimension to 500 ms                      bulletin                      .                      jms_expiration                      =                      500                    
jms_message_id Same as Java's JMSMessageID. Automatically set by JmsTemplate for received messages, may exist gear up manually just the value will exist ignored by the JMS provider.
jms_redelivered Aforementioned every bit Java'southward JMSRedelivered header. Should not exist set up manually. Default value for incoming messages is Imitation; for messages received from WebSphere MQ (which is currently the only supported JMS provider) it will be True if the underlying MQ message's BackoutCount attribute is 1 or greater.
jms_reply_to

Equivalent to Java'due south JMSReplyTo, the name of a JMS destination to which responses to the currently sent bulletin should be delivered:

                      message                      =                      TextMessage                      (                      "Please, reply to me."                      )                      # Set the reply to queue                      message                      .                      jms_reply_to                      =                      "REPLY.TO.QUEUE.1"                    

Run into hither for an instance of how to use jms_reply_to in asking/respond scenarios.

jms_timestamp Same every bit Java's JMSTimestamp, the timestamp of a bulletin returned every bit a number of milliseconds with a centiseconds precision. Should non be set manually.
max_chars_printed

Specifies how many characters of the concern payload (the .text attribute) will exist returned past the TextMessage case's __str__ method, which is used, for case, for logging purposes. Default value is 100 characters.

Consider the code below, in both cases the bulletin's content is the same, the letters differ only by the value of the max_chars_printed aspect:

                      # Spring Python                      from                      springpython.jms.cadre                      import                      TextMessage                      payload                      =                      "Business payload. "                      *                      eight                      msg                      =                      TextMessage                      (                      payload                      )                      msg                      .                      max_chars_printed                      =                      50                      print                      msg                    
JMS message class: jms_text   jms_delivery_mode:  two   jms_expiration:     None   jms_priority:       None   jms_message_id:     None   jms_timestamp:      None   jms_correlation_id: None   jms_destination:    None   jms_reply_to:       None   jms_redelivered:    None Business payload. Business payload. Business paylo Another 94 character(s) omitted
                      # Spring Python                      from                      springpython.jms.core                      import                      TextMessage                      payload                      =                      "Business payload. "                      *                      viii                      msg                      =                      TextMessage                      (                      payload                      )                      msg                      .                      max_chars_printed                      =                      20                      print                      msg                    
JMS message class: jms_text   jms_delivery_mode:  2   jms_expiration:     None   jms_priority:       None   jms_message_id:     None   jms_timestamp:      None   jms_correlation_id: None   jms_destination:    None   jms_reply_to:       None   jms_redelivered:    None Business payload. Bu Another 124 character(s) omitted

Attributes shown in the table above are standard JMS headers, available regardless of the JMS provider used. For WebSphereMQ - which is currently the merely JMS provider supported by Spring Python - following attributes are also available: JMS_IBM_Report_Exception, JMS_IBM_Report_Expiration, JMS_IBM_Report_COA, JMS_IBM_Report_COD, JMS_IBM_Report_PAN, JMS_IBM_Report_NAN, JMS_IBM_Report_Pass_Msg_ID, JMS_IBM_Report_Pass_Correl_ID, JMS_IBM_Report_Discard_Msg, JMSXGroupID, JMSXGroupSeq, JMS_IBM_Feedback, JMS_IBM_Last_Msg_In_Group, JMSXUserID, JMS_IBM_PutTime, JMS_IBM_PutDate and JMSXAppID. Refer to the IBM'due south Java JMS documentation for info on how to use them.

Creating custom JMS properties is simply a matter of assigning a value to an aspect, there are no special methods such as setStringProperty/getStringProperty which are used in Coffee JMS, thus the following code volition create a custom MESSAGE_NAME property which can be read past getStringProperty on the Coffee side:

              # Bound Python              from              springpython.jms.core              import              TextMessage              msg              =              TextMessage              (              "Hello!"              )              msg              .              MESSAGE_NAME              =              "HelloRequest"            

Observe how custom properties will be printed to the console along with standard JMS headers:

              # Spring Python              from              springpython.jms.cadre              import              TextMessage              msg              =              TextMessage              (              "Howdy!"              )              msg              .              MESSAGE_NAME              =              "HelloRequest"              msg              .              CLIENT              =              "CRM"              msg              .              CUSTOMER_ID              =              "201888228"              print              msg            
JMS message class: jms_text   jms_delivery_mode:  2   jms_expiration:     None   jms_priority:       None   jms_message_id:     None   jms_timestamp:      None   jms_correlation_id: None   jms_destination:    None   jms_reply_to:       None   jms_redelivered:    None   CLIENT:CRM   CUSTOMER_ID:201888228   MESSAGE_NAME:HelloRequest How-do-you-do!

Not all TextMessage's attributes can be set to a custom value, the exact list of reserved attributes' names is available as springpython.jms.core.reserved_attributes. There's a very slim chance you'll ever run across the disharmonize with your application'southward message attributes, yet be sure to check the list before using custom JMS properties in your code.

9.8. Exceptions¶

springpython.jms.JMSException is the base exception class for all JMS-related problems that may be raised by Jump Python'south JMS and a pair of its specialized subclasses is also available: springpython.jms.NoMessageAvailableException and springpython.jms.WebSphereMQJMSException.

NoMessageAvailableException is raised when a call to receive or receive_and_convert timeouts, which indicates that there's no message available for a given JMS destination.

WebSphereMQJMSException is raised when the underlying error is known to be caused by a phone call to WebSphere MQ API, such as a call to connect to a queue manager. Leap Python tries to populate these attributes of a WebSphereMQJMSException object when an fault condition arises:

  • message - a descriptive information of what has happened; taken either from an exception raised deeper in a telephone call stack or an caption from Bound Python itself,
  • completion_code - an integer code returned past the call a queue manager, may be either i (a alarm) or 2 (an error), it's known as an MQCC in WebSphere MQ,
  • reason_code - an integer code, as returned past the queue manager, giving a reason for the failure, known equally MQRC in WebSphere MQ lingo. The pregnant may be looked up in the IBM'southward "WebSphere MQ Constants" manual.

Note that message, completion_code and reason_code are all optional and there'due south no guarantee they volition be actually returned. Should yous caught a WebSphereMQJMSException, you should always check for their existence before making whatever use of them.

ix.9. Logging and troubleshooting¶

Spring Python's JMS uses standard Python's logging module for emitting the messages. In full general, you can await JMS to behave sane, information technology won't overflow your logs with meaningless entries, e.m. if you configure it to log the messages at the Mistake level then you'll exist notified of simply truly erratic situtations.

In addition to logging's builtin levels, JMS uses ane custom level - springpython.util.TRACE1, enabling TRACE1 will dethrone the performance considerably and will outcome in a huge number of messages written to the logs. Utilise it sparingly at troubleshooting times when you'd similar to encounter the exact flow of messages, raw bytes and JMS headers passing past the Leap Python's JMS classes involved. Do not always enable it in production environments unless you have a very compelling reason and you're sure you lot're comfortable with paying the functioning penalization. Consider using the logging.DEBUG level instead of TRACE1 if all you're afterwards is merely seeing the messages' payload.

JMS loggers currently employed by Spring Python are springpython.jms.manufacturing plant.WebSphereMQConnectionFactory, springpython.jms.listener.SimpleMessageListenerContainer and springpython.jms.listener.WebSphereMQListener(LISTENER_INSTANCE_ID).

Hither's how the WebSphere MQ connectedness factory'southward logger can be configured to work at the INFO level:

              # stdlib              import              logging              log_format              =              "              %(asctime)s                              -                            %(levelname)s                              -                            %(process)d                              -                            %(threadName)southward                              -                            %(name)s                              -                            %(bulletin)southward              "              formatter              =              logging              .              Formatter              (              log_format              )              handler              =              logging              .              StreamHandler              ()              handler              .              setFormatter              (              formatter              )              jms_logger              =              logging              .              getLogger              (              "springpython.jms.factory.WebSphereMQConnectionFactory"              )              jms_logger              .              setLevel              (              level              =              logging              .              INFO              )              jms_logger              .              addHandler              (              handler              )            

Here'due south how to configure it to log messages at the TRACE1 level:

              # stdlib              import              logging              # Spring Python              from              springpython.util              import              TRACE1              log_format              =              "              %(asctime)s                              -                            %(levelname)s                              -                            %(process)d                              -                            %(threadName)southward                              -                            %(name)s                              -                            %(message)s              "              formatter              =              logging              .              Formatter              (              log_format              )              handler              =              logging              .              StreamHandler              ()              handler              .              setFormatter              (              formatter              )              jms_logger              =              logging              .              getLogger              (              "springpython.jms.manufactory.WebSphereMQConnectionFactory"              )              jms_logger              .              setLevel              (              level              =              TRACE1              )              jms_logger              .              addHandler              (              handler              )            

springpython.jms.listener.SimpleMessageListenerContainer is the logger used by the JMS listener container itself.

Each WebSphere MQ listener is assigned a springpython.jms.listener.WebSphereMQListener(LISTENER_INSTANCE_ID) logger, where LISTENER_INSTANCE_ID is an identifier uniquely associated with a listener to form a full proper noun of a logger, such equally springpython.jms.listener.WebSphereMQListener(0xc7f5e0). To exist precise, its value is obtained by invoking hex(id(self)) on the listener'southward case. Note that the value is not guaranteed to be globally unique, it's but an identifier of the Python object so its value may be very well reused across application's restarts.

How much information is existence logged depends on the logging level, the boilerplate message size, the messages' max_chars_printed attribute value and the message rate.

Hither's an estimation of how fast log files will abound depending on the logging level. During the exam, the message size was 5kB, there were a total of ten,000 letters sent, the max_chars_printed aspect had value of 100 and the log entries were written to an ordinary log file:

  • Fault - 0KB, no errors were encountered thus no entries were written to the log file,
  • INFO - 0.9KB, only very basic info was logged, such as events of connecting to and disconnecting from a JMS provider,
  • DEBUG - vii,3MB, upward to the max_chars_printed characters of each message were written to the file plus all of JMS headers and some additional info as well,
  • TRACE1 - 79MB, total trace was taken which resulted in the log file's growing more than tenfold as compared to the DEBUG level.

smithdowen1979.blogspot.com

Source: https://docs.spring.io/spring-python/1.2.x/sphinx/html/jms.html

0 Response to "Cab Python 3 Read From Jms Queue"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel