Chapter 21. The javax.jms PackageThe javax.jms package implements the Java Message Service (JMS), which provides an API for message-based communication between separate Java processes. Message-based communication is asynchronous; a message addressed to a recipient or group is sent, and the recipient receives and acts on the message at some later time. This is different from other network-based communication between clients, like RMI, where the sender of a message waits for a response before continuing. In the JMS model, clients of a message service send and receive messages through a provider that is responsible for delivering messages. JMS 1.0 provides two models for messaging among clients: point-to-point and publish/subscribe. In point-to-point messaging, a message is created by one client and addressed for a single remote recipient. The provider is handed the message and delivers it to the one recipient targeted by the message. This model revolves around message queues: a message sender queues outgoing messages for delivery and a message recipient queues incoming messages for handling. The interfaces provided in the javax.jms package for point-to-point messaging have "Queue" as their prefix (QueueConnection, QueueSession, etc.). In publish/subscribe messaging, a hierarchical content tree is established. Clients publish messages to specific nodes or topics in the tree, and these messages are delivered to any clients that have subscribed to these nodes. Interfaces related to publish/subscribe messaging have "Topic" as their prefix (TopicConnection, TopicSession, etc.). Point-to-point messaging is analogous to typical email messaging; publish/subscribe messaging works like Internet newsgroups. In a typical scenario, a JMS client gets a reference to a ConnectionFactory from the JMS provider (usually through a JNDI lookup). The ConnectionFactory creates a Connection to the provider. With the Connection, a client can create Session objects to send and receive messages. Within a single Session, messages are sent and received in a serial order. Once the client has a Session, it can send and receive Message objects composed of a header, optional properties, and a body. Different types of Message objects can hold different contents in their body (e.g., text, binary data, name/value pairs). Figure 21-1 shows the interfaces in the javax.jms package, while Figure 21-2 shows the classes and exceptions. Figure 21-1. The interfaces of the javax.jms packageFigure 21-2. The classes and exceptions of the javax.jms package
A BytesMessage is a Message that contains an uninterpreted stream of bytes as its body. This is typically used to wrap an existing (non-JMS) message format so that it can be delivered over JMS. Data is written to the message's binary body using its writeXXX() methods and read using its readXXX() methods. Once a BytesMessage has been created (using a series of write calls), the reset() method can put the message into read-only mode. Until this is done, the message is in write-only mode, and the contents cannot be read.
Hierarchy: (BytesMessage(Message)) Returned By: Session.createBytesMessage()
A JMS client needs to have a Connection to the JMS provider in order to send or receive messages. The javax.jms.Connection interface for messaging is roughly analogous to the java.sql.Connection interface in JDBC--one connects a client to a messaging service, while the other connects a client to a persistent data service. JMS Connection objects are generally expensive to create, because setup requires network communication with the provider. Thus, a client normally has only one, or very few, Connection objects to its JMS provider. A Connection can either be in running mode (messages are being sent and received through the connection), or it can be stopped. When a Connection is in stopped mode, it can send messages, but not receive them. A newly created Connection is in stopped mode so that you can finish setting up your client (e.g., creating Session objects; creating MessageConsumer and/or MessageProducer objects). A Connection can be started and stopped using stop() and start() multiple times, if necessary. When you're done with a Connection, you should free up its resources by calling its close() method. A Connection object creates sessions for message exchanges. The methods for creating sessions are defined by extensions of the Connection interface (QueueConnection and TopicConnection).
Implementations: QueueConnection, TopicConnection
A ConnectionConsumer is used in situations where messages need to be read concurrently by multiple agents within the same process (e.g., within an application server running multiple message-based applications). The ConnectionConsumer delivers messages to one or more Session objects that are associated with MessageListener objects for individual clients. It contains a reference to a ServerSessionPool that accesses the Session objects concurrently reading messages. ConnectionConsumer is typically used by an application server to provide a message-handling service for client applications; applications normally don't need to use the interface directly.
Returned By: QueueConnection.createConnectionConsumer(), TopicConnection.{createConnectionConsumer(), createDurableConnectionConsumer()}
A messaging client uses a ConnectionFactory to get a Connection object to a message provider. A ConnectionFactory is usually acquired through a JNDI lookup. The ConnectionFactory interface doesn't define methods for creating Connection objects; these methods are provided by extensions to this interface (e.g., QueueConnectionFactory, TopicConnectionFactory).
Implementations: QueueConnectionFactory, TopicConnectionFactory
The Connection.getMetaData() method returns a ConnectionMetaData object that holds information about the JMS connection, including the version of JMS being used by the provider and version information about the provider itself.
Returned By: javax.jms.Connection.getMetaData()
This interface defines constants that represent delivery modes a JMS provider can support. NON_PERSISTENT delivery implies that a significant failure by the provider before a message can be delivered causes the message to be lost. PERSISTENT delivery mode implies that the provider stores messages to persistent storage, so that the messages survive a crash by the provider. A message sender specifies the delivery mode for a Message in its header, using the Message.setJMSDeliveryMode() method, and the provider is responsible for honoring the delivery mode.
A Destination represents a delivery address for a message. This interface is simply a marker interface, without any methods or members, since JMS does not attempt to define an addressing syntax. An implementor of a JMS provider needs to provide implementations of this interface that define its message-addressing syntax. Destination objects might be published by a JMS provider using JNDI. In this case, a JMS client needs to assemble the name of the queue in the syntax expected by the provider and then do a JNDI lookup to get a reference to the Destination.
Implementations: Queue, Topic Passed To: Message.{setJMSDestination(), setJMSReplyTo()} Returned By: Message.{getJMSDestination(), getJMSReplyTo()}
An ExceptionListener gets asynchronous notification of errors that occur with a Connection to a JMS provider. If a client registers an ExceptionListener with a Connection using the Connection.setExceptionListener() method, the provider calls the onException() method on the ExceptionListener when any error occurs, passing it the exception that describes the error.
Passed To: javax.jms.Connection.setExceptionListener()
Thrown if a request is made of a provider at a time when the request cannot be satisfied.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->IllegalStateException
Thrown by the Connection.setClientID() method when an invalid client ID is given.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->InvalidClientIDException
Thrown when the provider encounters a destination it cannot understand or is no longer accessible by the provider (e.g., a topic has been removed from a publish/subscribe context, or a queue associated with a user account has been removed because the account has been closed).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->InvalidDestinationException
Thrown when a malformed selector is given to a provider (e.g., as part of a MessageSelector).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->InvalidSelectorException
This is the base class for all JMS-related exceptions. It provides a provider-specific error code and a nested exception that is the source of the error.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException Subclasses: javax.jms.IllegalStateException, InvalidClientIDException, InvalidDestinationException, InvalidSelectorException, JMSSecurityException, MessageEOFException, MessageFormatException, MessageNotReadableException, MessageNotWriteableException, ResourceAllocationException, TransactionInProgressException, TransactionRolledBackException Passed To: ExceptionListener.onException() Thrown By: Too many methods to list.
Thrown by a provider when a request cannot be satisfied for security reasons (e.g., a client-provided name/password fails authentication).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->JMSSecurityException
A MapMessage has a set of name/value pairs as its message body. The name of a property is a String, and the value is a Java primitive type. There are getXXX() and setXXX() methods for each primitive type, plus getObject() and setObject() methods for situations where the type of the value is not known until runtime. If a property is set to a value of a certain type, it has to be read back using a get method appropriate for that type, according to this table:
If a value is read using an inappropriate get method, a MessageFormatException is thrown. The getMapNames() methods returns an Enumeration of names for the values in the MapMessage. A client that receives a MapMessage can read only the contents of the message until it calls clearBody() on the message. If a client tries to write values to the message before this, a MessageNotWriteableException is thrown.
Hierarchy: (MapMessage(Message)) Returned By: Session.createMapMessage()
The Message interface is the base interface for all messages in JMS. A Message is composed of a set of predefined header fields, an optional set of application-specific properties, and a body that contains the content of the message. A set and get method are provided for each header field supported by a JMS message. The possible header fields include:
Properties with names prefixed by "JMSX" are reserved for use by the JMS standard. Properties can be added to a message using the setXXXProperty() methods and read using the getXXXProperty() methods. If a property is written with a value of a given type, it needs to be read from the message according to the following table:
If in invalid get method is used for a property, a MessageFormatException is thrown.
Implementations: BytesMessage, MapMessage, ObjectMessage, StreamMessage, TextMessage Passed To: MessageListener.onMessage(), QueueRequestor.request(), QueueSender.send(), TopicPublisher.publish(), TopicRequestor.request() Returned By: MessageConsumer.{receive(), receiveNoWait()}, QueueRequestor.request(), Session.createMessage(), TopicRequestor.request()
A JMS client uses a MessageConsumer to receive messages. A client creates a message consumer by specifying a Destination from which to receive messages and an optional message selector that filters messages according to their header fields and property values. The methods for creating MessageConsumer objects are defined in subinterfaces of the Connection interface. A message selector is a filter string whose syntax is based on the SQL92 conditional expression syntax. See the JMS specification for more details on the syntax of message selectors. A client can use a MessageConsumer synchronously by polling it with its receive methods or asynchronously by registering a MessageListener with the consumer. When a message arrives that matches the sending Destination and the message selector, the onMessage() method on the registered listener is called. A MessageConsumer should be freed by calling its close() method, to free up any resources allocated for it by the provider.
Implementations: QueueReceiver, TopicSubscriber
Thrown if the end of a StreamMessage or BytesMessage is reached before it is expected.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->MessageEOFException
Thrown when an attempt is made to read data from a message as the wrong data type or to write data to a message in a type it does not support.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->MessageFormatException
A MessageListener is registered by a client with a MessageConsumer, to allow the client to asynchronously receive messages. When the consumer receives a message, the listener's onMessage() method is invoked. The consumer waits until the onMessage() method is complete before delivering the next message.
Passed To: MessageConsumer.setMessageListener(), Session.setMessageListener() Returned By: MessageConsumer.getMessageListener(), Session.getMessageListener()
Thrown if a client attempts to read data from a write-only message (e.g., a StreamMessage whose contents have not yet been reset).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->MessageNotReadableException
Thrown when an attempt is made to write data to a read-only message (e.g., a received MapMessage that has not yet had its clearBody() method called).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->MessageNotWriteableException
A client uses a MessageProducer to send messages. A MessageProducer can be tied to a specific Destination, and any messages sent through the producer are addressed to the Destination specified when it was created. If a Destination is not specified when a MessageProducer is created, a Destination has to provide for each message sent. Methods to create MessageProducer objects are provided by subinterfaces of the Session interface (e.g., TopicSession, QueueSession). A MessageProducer has a default delivery mode, priority, and time-to-live for messages it sends. There are get and set methods for these default properties. If these properties are specified on a message, they override the defaults of the MessageProducer.
Implementations: QueueSender, TopicPublisher
This is a message that contains a single serialized Java object as its body. Only a Serializable object can be used as the body of an ObjectMessage. When an ObjectMessage is received, it is read-only until the clearBody() method is called on it.
Hierarchy: (ObjectMessage(Message)) Returned By: Session.createObjectMessage()
A Queue is a Destination specific to point-to-point messaging. The Queue has a String name whose syntax is dictated by the provider. Queue objects are created using the createQueue() method on a QueueSession.
Hierarchy: (Queue(Destination)) Implementations: TemporaryQueue Passed To: QueueConnection.createConnectionConsumer(), QueueRequestor.QueueRequestor(), QueueSender.send(), QueueSession.{createBrowser(), createReceiver(), createSender()} Returned By: QueueBrowser.getQueue(), QueueReceiver.getQueue(), QueueSender.getQueue(), QueueSession.createQueue()
A QueueBrowser peeks at the contents of a message queue without actually removing messages. A QueueBrowser has an optional message selector that can filter the messages checked for on the queue. QueueBrowser objects are created using the createBrowser() methods on QueueSession.
Returned By: QueueSession.createBrowser()
A QueueConnection is a Connection specific to a point-to-point messaging provider. The QueueConnection allows clients to create QueueSession objects using the createQueueSession() method.
Hierarchy: (QueueConnection(javax.jms.Connection)) Implementations: XAQueueConnection Returned By: QueueConnectionFactory.createQueueConnection()
A QueueConnectionFactory is exported by point-to-point providers to allow clients to create QueueConnection objects to the provider. The default createQueueConnection() method creates a connection under the default user identity of the client JVM, while the other constructor accepts a name and password that authenticates the connection request.
Hierarchy: (QueueConnectionFactory(ConnectionFactory)) Implementations: XAQueueConnectionFactory
A QueueReceiver is a MessageConsumer specific to point-to-point messaging. The getQueue() method returns the Queue associated with the receiver. QueueReceiver objects are created using the createReceiver() methods on QueueSession.
Hierarchy: (QueueReceiver(MessageConsumer)) Returned By: QueueSession.createReceiver()
QueueRequestor is a utility class provided for situations in which a client wants to send a message to a specific destination and wait for a response. A QueueRequestor is constructed with a QueueSession and a destination Queue, and then its request() method is called with the Message to be sent. The QueueRequestor sets the reply-to destination on the message to a temporary Queue that it creates. It sends the message and waits for a response. The response Message is the return value of the request() method.
A QueueSender is a MessageProducer that sends messages in a point-to-point context. QueueSender objects are created using the createQueue() method on QueueSession, specifying a default Queue as the target of messages. A client can override the default message target by using one of the send() methods on the QueueSender that accepts a target Queue. If a send() method is called without a target Queue, and the QueueSender does not have a default target Queue defined (e.g., it was created with a null target Queue), an InvalidDestinationException is thrown.
Hierarchy: (QueueSender(MessageProducer)) Returned By: QueueSession.createSender()
The QueueSession is a Session specific to a point-to-point messaging context. It provides methods for creating point-to-point message consumers (QueueReceiver), producers (QueueSender), and destinations (Queue), as well as utilities objects such as QueueBrowser and TemporaryQueue.
Hierarchy: (QueueSession(Session(Runnable))) Passed To: QueueRequestor.QueueRequestor() Returned By: QueueConnection.createQueueSession(), XAQueueSession.getQueueSession()
Thrown when a request is made of a provider, and it cannot be completed due to resource issues.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->ResourceAllocationException
A ServerSession is used by an application server when it needs to separate Session objects to individual threads, for concurrent access to and handling of flows of messages. The ServerSession represents a JMS Session tied to a thread. A ConnectionConsumer keeps a pool of ServerSession objects, which it handles messages as they arrive. The ConnectionConsumer assigns one or more messages to the Session contained in the ServerSession, and then calls the ServerSession's start() method. The ServerSession calls start() on the Thread for the Session, which eventually calls the run() method of the Session (Session implements the java.lang.Runnable interface).
Returned By: ServerSessionPool.getServerSession()
A ConnectionConsumer uses a ServerSessionPool to manage a pool of ServerSession objects. The ServerSessionPool can manage the pool any way it likes and can block if the pool is exhausted.
Passed To: QueueConnection.createConnectionConsumer(), TopicConnection.{createConnectionConsumer(), createDurableConnectionConsumer()} Returned By: ConnectionConsumer.getServerSessionPool()
A Session provides a client with the means for creating messages, message producers, and message consumers. Extensions of Session create type-specific versions of these objects (e.g., QueueSender and TopicPublisher). Within a Session, messages are sent and received in a serial order. The Session interface also provides facilities for JMS providers that chose to provide transactional support in their Session implementations. A transaction is started when the Session is created. In a messaging context, a transaction consists of a series of message transmissions and receipts. Committing a messaging transaction (by calling commit() on the corresponding Session) causes all pending transmissions to be sent and all pending receipts to be finalized and acknowledged. If a transaction is aborted (by calling the rollback() method on the Session), the outgoing messages are destroyed, and incoming messages are cancelled. A new transaction is started as soon as the current one is committed or rolled back.
Hierarchy: (Session(Runnable)) Implementations: QueueSession, TopicSession, XASession Returned By: ServerSession.getSession()
A StreamMessage is a Message whose body consists of a stream of serialized Java primitive data items. It is similar in many ways to a BytesMessage, except that the contents of a StreamMessage are read in the same order they are written by the sender. Otherwise, StreamMessage has a similar set of read/write methods and similar rules about how certain data types are read from the message as BytesMessage.
Hierarchy: (StreamMessage(Message)) Returned By: Session.createStreamMessage()
A TemporaryQueue is a Queue used internally by a QueueConnection.
Hierarchy: (TemporaryQueue(Queue(Destination))) Returned By: QueueSession.createTemporaryQueue()
A TemporaryTopic is a Topic used internally by a TopicConnection.
Hierarchy: (TemporaryTopic(Topic(Destination))) Returned By: TopicSession.createTemporaryTopic()
A TextMessage is a Message whose body is a String. The contents of the message can be retrieved using the getText() method. The text of the message might be simple ASCII, or it could be structured according to a syntax like HTML or XML.
Hierarchy: (TextMessage(Message)) Returned By: Session.createTextMessage()
A Topic is an address for a message in a publish/subscribe context. The Topic interface simply defines a String name for the topic. Providers define how topics are defined and grouped into a hierarchy.
Hierarchy: (Topic(Destination)) Implementations: TemporaryTopic Passed To: TopicConnection.{createConnectionConsumer(), createDurableConnectionConsumer()}, TopicPublisher.publish(), TopicRequestor.TopicRequestor(), TopicSession.{createDurableSubscriber(), createPublisher(), createSubscriber()} Returned By: TopicPublisher.getTopic(), TopicSession.createTopic(), TopicSubscriber.getTopic()
A TopicConnection is a Connection to a publish/subscribe-based JMS provider. It provides methods for creating TopicSession objects, as well as ConnectionConsumer objects.
Hierarchy: (TopicConnection(javax.jms.Connection)) Implementations: XATopicConnection Returned By: TopicConnectionFactory.createTopicConnection()
A TopicConnectionFactory is exported by publish/subscribe providers to allow clients to create TopicConnection objects to the provider. The default createTopicConnection() method creates a connection under the default user identity of the client JVM, while the other constructor accepts a name and password that authenticates the connection request.
Hierarchy: (TopicConnectionFactory(ConnectionFactory)) Implementations: XATopicConnectionFactory
A TopicPublisher is a MessageProducer specific to a publish/subscribe context. TopicPublisher objects are created using the createPublisher() method on a TopicSession. A TopicPublisher is created with a Topic under which it publishes messages. A client can override the default Topic using one of the publish() methods that accepts a Topic as an argument along with the Message to be sent. Sending a Message without a Topic (i.e., the TopicPublisher was created with a nullTopic, and the Message was sent without specifying a Topic) causes an InvalidDestinationException to be thrown.
Hierarchy: (TopicPublisher(MessageProducer)) Returned By: TopicSession.createPublisher()
TopicRequestor is a utility class provided for situations where a client wants to send a message to a specific Topic and wait for a response. The TopicRequestor is constructed with a TopicSession and a destination Topic, and then its request() method is called with the Message to be sent. The TopicRequestor sets the reply-to destination on the message to a temporary Topic it creates. It sends the message and waits for a response. The response Message is the return value of the request() method.
A TopicSession is a Session specific to a publish/subscribe context. It provides methods for creating publish/subscribe message consumers (TopicSubscriber), message producers (TopicPublisher), and message destinations (Topic). It also has methods for creating utilities objects such as TemporaryTopic.
Hierarchy: (TopicSession(Session(Runnable))) Passed To: TopicRequestor.TopicRequestor() Returned By: TopicConnection.createTopicSession(), XATopicSession.getTopicSession()
A TopicSubscriber is a MessageConsumer specific to a publish/subscribe context. TopicSubscriber objects are created using the createSubscriber() and createDurableSubscriber() methods on the TopicSession. A TopicSubscriber is created with a Topic to subscribe to and can optionally be created with a message selector that filters the messages received by the subscriber. If a client is both publishing and subscribing to the same Topic, the no-local attribute on the TopicSubscriber specifies whether to filter out messages published by the same connection. If a TopicSubscriber is created as durable (using createDurableSubscriber() on the TopicSession), the provider collects messages for this subscriber even when the subscriber is inactive. The provider keeps these messages until the subscriber receives them, or until they expire according to the sender's time-to-live header attribute. In order for the client to retrieve the messages collected under a durable TopicSubscriber after it has reactivated itself, it has to create a new TopicSubscriber under the same Topic with the same client ID.
Hierarchy: (TopicSubscriber(MessageConsumer)) Returned By: TopicSession.{createDurableSubscriber(), createSubscriber()}
Thrown if an invalid request is made during a transactional session (e.g., attempting to commit() a session while a message is still being sent).
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->TransactionInProgressException
Thrown by the Session.commit() method if the transaction needs to be rolled back because of some internal error.
Hierarchy: Object-->Throwable(Serializable)-->Exception-->JMSException-->TransactionRolledBackException
This interface represents a Connection to a provider that supports transactional messaging, according to the X/Open XA protocol for transactional processing. Subinterfaces of XAConnection generate transactional sessions in the form of XASession objects.
Implementations: XAQueueConnection, XATopicConnection
A transactional JMS provider exports an XAConnectionFactory for clients to use to create XAConnection objects. Clients typically find a provider's XAConnectionFactory using a JNDI lookup.
Implementations: XAQueueConnectionFactory, XATopicConnectionFactory
An XAQueueConnection is a Connection to a transactional provider of point-to-point messaging. It extends the QueueConnection interface with a createXAQueueSession() method, which creates a transactional XAQueueSession.
Hierarchy: (XAQueueConnection(javax.jms.XAConnection,QueueConnection(javax.jms.Connection))) Returned By: XAQueueConnectionFactory.createXAQueueConnection()
An XAQueueConnectionFactory is a QueueConnectionFactory that creates XAQueueConnection objects to a transactional point-to-point JMS provider.
Hierarchy: (XAQueueConnectionFactory(XAConnectionFactory,QueueConnectionFactory(ConnectionFactory)))
An XAQueueSession is a wrapper around a QueueSession. It represents a transactional session with a JMS point-to-point provider.
Hierarchy: (XAQueueSession(XASession(Session(Runnable)))) Returned By: XAQueueConnection.createXAQueueSession()
An XASession is a Session with a provider that supports transactional messaging according to the X/Open XA protocol for transactional processing. The XASession contains a javax.transaction.xa.XAResource object that represents the association of the Session with a transaction context.
Hierarchy: (XASession(Session(Runnable))) Implementations: XAQueueSession, XATopicSession
An XATopicConnection represents a Connection to a transactional provider of publish/subscribe messaging. It extends the TopicConnection interface with a createXATopicSession() method, which creates a transactional XATopicSession.
Hierarchy: (XATopicConnection(javax.jms.XAConnection,TopicConnection(javax.jms.Connection))) Returned By: XATopicConnectionFactory.createXATopicConnection()
An XATopicConnectionFactory is a TopicConnectionFactory that creates XATopicConnection objects to a transactional publish/subscribe JMS provider.
Hierarchy: (XATopicConnectionFactory(XAConnectionFactory,TopicConnectionFactory(ConnectionFactory)))
An XATopicSession is a wrapper around a TopicSession. It represents a transactional session with a JMS publish/subscribe provider.
Hierarchy: (XATopicSession(XASession(Session(Runnable)))) Returned By: XATopicConnection.createXATopicSession() Copyright © 2001 O'Reilly & Associates. All rights reserved. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|