Jakarta Messaging
Jakarta Messaging is a Java application programming interface (API) specification within the Jakarta EE platform that enables applications to create, send, receive, and read messages using message-oriented middleware (MOM) for reliable, asynchronous, and loosely coupled communication.[1] Originally developed as the Java Message Service (JMS) under Java EE, Jakarta Messaging emerged as part of the transition of Java EE technologies to the Eclipse Foundation in 2017, with the project rebranded under the Jakarta EE umbrella to reflect open governance and community-driven evolution.[2] The specification defines a portable set of interfaces and semantics that abstract the underlying messaging provider, allowing developers to build distributed systems without tight coupling between producers and consumers of messages.[3] Key components of Jakarta Messaging include connections, which establish virtual links to a messaging provider; sessions, which provide a single-threaded context for producing and consuming messages; messages, structured objects containing headers, properties, and a body for data transfer; and destinations, such as queues for point-to-point (PTP) messaging or topics for publish/subscribe (pub/sub) models.[3] It supports two primary messaging domains: PTP, where messages are delivered to a single consumer from a queue, and pub/sub, where messages are broadcast to multiple subscribers via topics, ensuring once-only delivery guarantees and transactional integrity.[3] Providers, such as Apache ActiveMQ or IBM MQ, implement these interfaces to handle the actual transport and persistence of messages.[4] The API integrates seamlessly with other Jakarta EE components, including message-driven beans (MDBs) for container-managed asynchronous processing and resource adapters for accessing external messaging systems.[5] Notable features include configurable acknowledgment modes (e.g., AUTO_ACKNOWLEDGE for automatic handling), support for both client and container-managed transactions, and mechanisms for delayed or shared subscriptions to enhance scalability in enterprise environments.[3] As of its latest release, Jakarta Messaging 3.1.0 (March 15, 2022), the specification has matured through versions aligned with Jakarta EE 8, 9, 10, and 11, emphasizing portability, reliability, and compatibility with modern Java features like modules and records.[6][7] It remains a foundational technology for building resilient, event-driven applications in distributed systems, widely adopted in industries for tasks ranging from microservices communication to legacy system integration.[8]Introduction
Definition and Purpose
Jakarta Messaging is a set of standard Java interfaces and associated semantics that define how Java programs create, send, receive, and read messages within enterprise messaging systems.[9] It provides a common API for message-oriented middleware, enabling applications to interact with various messaging providers in a portable manner.[2] The primary purpose of Jakarta Messaging is to facilitate loosely coupled, asynchronous communication between distributed Java components, which promotes scalability and fault tolerance in enterprise environments.[1] By decoupling senders and receivers, it allows systems to handle varying loads without direct dependencies, buffering messages to manage peak demands effectively.[3] Additionally, it integrates seamlessly with other Jakarta EE technologies, such as transactions and enterprise beans, to support reliable business process orchestration.[9] Jakarta Messaging evolved from the original Java Message Service (JMS) specification, transitioning to the Jakarta namespace as part of the broader shift of Java EE to the Eclipse Foundation in 2017.[9] This change reflects ongoing efforts to modernize the API for contemporary enterprise needs while maintaining backward compatibility with JMS implementations.[2]Development History
The Java Message Service (JMS) specification was initially released as version 1.0 in 1998 by Sun Microsystems as part of the emerging Java EE platform, providing a standardized API for messaging in Java applications.[10] This initial version laid the foundation for asynchronous communication between distributed components. Subsequent patch releases addressed clarifications and minor issues, including JMS 1.0.1 in October 1998, JMS 1.0.2 in December 1999, and JMS 1.0.2b in August 2001, ensuring compatibility and refining implementation details without introducing major new features.[11][12] A significant evolution occurred with JMS 1.1 in March 2002, which introduced domain-independent APIs to unify point-to-point and publish-subscribe models under a common interface.[10] This update, developed under JSR 914, enhanced portability and simplified development for enterprise applications.[12] The specification then saw a decade of stability until JMS 2.0 was released in May 2013 as part of Java EE 7, adding capabilities such as shared durable subscriptions for load balancing across consumers, support for shared subscriptions, and simplified resource injection using annotations like @Resource.[13][14] In November 2017, Oracle announced the transfer of Java EE stewardship to the Eclipse Foundation to foster open community-driven development, with the process completing by 2019 alongside the release of Jakarta EE 8, which retained the javax.* namespace for backward compatibility.[15] This rebranding marked the shift to Jakarta EE, culminating in Jakarta Messaging 3.0 in November 2020 for Jakarta EE 9, which mandated the jakarta.jms namespace and required providers to support both messaging domains natively.[16] Jakarta Messaging 3.1 followed in March 2022 for Jakarta EE 10, incorporating minor clarifications and compatibility updates.[6][17] As of November 2025, Jakarta Messaging 3.1 is included in Jakarta EE 11 (released June 2025) and remains under active development by the Eclipse Foundation's Jakarta EE Working Group, with ongoing enhancements for alignment with modern Java platforms like Java SE 21 while maintaining compatibility with prior releases.[18][2]Fundamental Components
Messages
In Jakarta Messaging, messages represent the core units of data exchange, encapsulating information for asynchronous communication between distributed applications. Each message adheres to a standardized structure defined by the jakarta.jms.Message interface, which serves as the root for all message types and ensures portability across compliant providers. This interface specifies the message header, properties, and body, enabling consistent handling of metadata and payload while supporting various data formats.[17] The anatomy of a Jakarta Messaging message includes a required header, optional properties, and an optional body. The header consists of predefined fields that facilitate routing, identification, and delivery control; these fields are typically set by the messaging provider or the sending application. Standard header fields are outlined in the following table:| Field Name | Type | Description | Set By |
|---|---|---|---|
| JMSDestination | Destination | The queue or topic to which the message is sent. | Jakarta Messaging provider send method |
| JMSMessageID | String | A unique identifier assigned to the message. | Jakarta Messaging provider send method |
| JMSTimestamp | long | The time at which the message was handed off to the provider for delivery. | Jakarta Messaging provider send method |
| JMSCorrelationID | String or byte[] | A value linking this message to another (e.g., for request-reply patterns). | Client |
| JMSReplyTo | Destination | The destination for a response message. | Client |
| JMSDeliveryMode | int | Indicates persistent (2) or non-persistent (1) delivery. | Jakarta Messaging provider send method |
| JMSRedelivered | boolean | True if the message is being redelivered. | Jakarta Messaging provider prior to delivery |
| JMSType | String | A message type identifier for filtering or routing. | Client |
| JMSExpiration | long | Expiration time (milliseconds since epoch), 0 for no expiration. | Jakarta Messaging provider send method |
| JMSPriority | int | Delivery priority from 0 (lowest) to 9 (highest), default 4. | Jakarta Messaging provider send method |
| JMSDeliveryTime | long | Scheduled delivery time (milliseconds since epoch), 0 for immediate. | Jakarta Messaging provider send method |
- TextMessage: Holds a single String, commonly used for text-based payloads like XML or JSON.[3]
- BytesMessage: Contains a stream of uninterpreted bytes, suitable for binary data.[22]
- StreamMessage: Stores a sequence of Java primitive values (e.g., int, double) in a write-once, read-many format.
- MapMessage: Encapsulates a set of name-value pairs with String keys and primitive or String values, akin to a portable hash map.[23]
- ObjectMessage: Transports a single serializable Java object.
- Message: The base type with no body, relying solely on headers and properties.[19]
JMSType = 'order' AND priority > 5 AND color LIKE 'red%' would match messages of type "order" with priority above 5 and a "color" property starting with "red". Only valid property types are evaluable, and selectors are evaluated by the provider for efficiency.[25][19]
Destinations
In Jakarta Messaging, destinations serve as the primary endpoints for message routing and consumption, abstracting the underlying provider-specific addressing mechanisms. Thejakarta.jms.Destination interface defines a runtime entity that encapsulates a provider-specific address, enabling applications to send messages to a target and receive them from a source without direct exposure to implementation details. This interface is portable across different messaging providers, allowing concurrent use by multiple threads and hiding administrative configuration behind a standardized Java object.
Queues represent destinations tailored for the point-to-point messaging paradigm, functioning as a temporary holding area where messages await consumption by a single receiver. A jakarta.jms.Queue object wraps a provider-specific queue name, ensuring that each message is delivered to exactly one consumer in a load-balanced manner, with retention until explicit consumption or expiration occurs. This design supports reliable, ordered delivery scenarios, such as task distribution in enterprise systems.[3]
In contrast, topics facilitate the publish-subscribe model by enabling one-to-many message distribution to multiple subscribers. The jakarta.jms.Topic interface, which extends Destination, encapsulates a provider-specific topic name and supports hierarchical structures for flexible subscription patterns, allowing messages to be broadcast instantaneously to all active subscribers without persistent storage beyond delivery. Topics are ideal for event notification and decoupled communication, where publishers remain unaware of the number or identity of consumers.[3]
Destinations are typically administered objects, meaning they are pre-configured by system administrators using provider tools or deployment descriptors and stored in a JNDI namespace for lookup by client applications. This approach ensures centralized management of configuration details, such as capacity limits and security settings, while allowing injection via annotations like @Resource in Jakarta EE environments. Administered destinations promote portability and ease of maintenance in distributed systems.[3]
For scenarios requiring ephemeral communication, temporary destinations provide dynamically created queues or topics with lifetimes bound to the creating connection. These are generated via methods like JMSContext.createTemporaryQueue() or createTemporaryTopic(), accessible only to consumers on the same connection, and automatically deleted upon connection closure, making them suitable for patterns like request-reply without persistent resource overhead.[3]
Connections and Sessions
In Jakarta Messaging, theConnectionFactory serves as the primary administered object for creating connections to a messaging provider. It encapsulates configuration parameters such as authentication credentials and connection pooling options, and can be obtained via Java Naming and Directory Interface (JNDI) lookups or through direct instantiation in non-Jakarta EE environments. Specific subtypes include QueueConnectionFactory for point-to-point messaging and TopicConnectionFactory for publish-subscribe scenarios, enabling tailored connection creation methods like createConnection() or createQueueConnection().[26]
A Connection object represents an active communication link between the client application and the messaging provider, typically implemented as a TCP/IP socket or similar transport. Upon creation via a ConnectionFactory, the connection starts in a stopped state, requiring an explicit call to start() to begin message flow to associated consumers; conversely, stop() suspends delivery without closing the connection. Connections also support setting a unique clientID via setClientID(), which is essential for enabling durable subscriptions in publish-subscribe models, though this method is restricted in Jakarta EE containers to prevent conflicts. Additionally, connections can register an ExceptionListener to handle provider-specific errors asynchronously.[26]
The Session interface provides a single-threaded context for producing and consuming messages within a connection, ensuring thread safety by prohibiting concurrent use across multiple threads. Created through methods like createSession(boolean transacted, int acknowledgeMode) on the connection, sessions operate in either transacted mode—for atomic message groups managed by commit() and rollback()—or non-transacted mode, where acknowledgment modes such as AUTO_ACKNOWLEDGE or CLIENT_ACKNOWLEDGE handle delivery guarantees. Sessions are responsible for instantiating MessageProducer, MessageConsumer, and Message objects, serving as the factory for these core messaging components. In the simplified API introduced in Jakarta Messaging 2.0 and refined in 3.0, sessions are implicitly managed within a JMSContext for streamlined usage.[26]
MessageProducer is the interface for sending messages to a specified destination, such as a queue or topic, and is obtained via session.createProducer(Destination destination). It supports both synchronous sends using send() methods and asynchronous delivery, with options to disable message IDs or timestamps for performance optimization. Similarly, MessageConsumer facilitates message receipt from a destination, created via session.createConsumer(Destination destination), and offers synchronous reception through receive() or asynchronous handling by setting a MessageListener with setMessageListener(). Both producers and consumers are tightly bound to their destinations at creation, promoting a destination-centric messaging model.[26]
Jakarta Messaging 3.0 marked a significant evolution in the API namespace, transitioning from the javax.jms package used in prior JMS versions to jakarta.jms to align with the Eclipse Foundation's governance of the Jakarta EE platform. This change necessitates code updates, such as replacing import statements and fully qualified class names, to ensure compatibility with Jakarta EE 9 and later environments, though backward compatibility layers may be provided by implementations.[26]
Messaging Paradigms
Point-to-Point Model
The point-to-point (PTP) model in Jakarta Messaging facilitates one-to-one message delivery, where messages are sent to a specific queue and consumed by exactly one consumer, enabling load balancing across multiple active consumers on the same queue.[9] This model uses queues as destinations, which store messages until they are successfully processed, supporting both synchronous and asynchronous consumption to decouple producers from consumers.[3] Queues encapsulate provider-specific addressing details and can handle concurrent access, though their capacity and overflow behavior are implementation-dependent rather than specified.[9] In the PTP domain, the legacy domain-specific interfaces QueueSender and QueueReceiver provide targeted operations for sending and receiving messages, respectively; these were primary in pre-2.0 versions but are now selectable via the unified Destination interface in Jakarta Messaging 3.0.[9] A QueueSender, created from a QueueSession, directs messages to a designated queue, while a QueueReceiver, also from a QueueSession, retrieves them and supports message selectors for filtering based on header or property values.[9] This approach allows for precise control in queue-based interactions, with the unified API introduced in JMS 2.0 (and retained in Jakarta Messaging 3.0) enabling code portability across PTP and publish-subscribe paradigms without domain-specific dependencies.[9] Common use cases for the PTP model include request-reply patterns, where a producer sends a request to a queue and awaits a response from a single consumer, and work queues for distributing tasks among worker processes to achieve load balancing.[3] For instance, in order processing systems, messages representing customer orders are queued for exclusive handling by one fulfillment service, ensuring no duplication.[27] Consumer behavior in the PTP model emphasizes exclusive delivery: messages are retained in the queue until acknowledged by the consuming session or until they expire, with delivery load-balanced to one of multiple active consumers to optimize throughput.[9] Asynchronous consumption via a MessageListener allows non-blocking processing, where the listener method is invoked upon message arrival, while synchronous receive methods block until a message is available or a timeout occurs.[3] If acknowledgment fails, the message may be redelivered, promoting reliability without specifying exact retry mechanisms, which are provider-specific. Unlike JMS 1.x, where PTP implementation was optional alongside the domain-specific APIs, Jakarta Messaging 3.0 mandates support for the PTP model in conjunction with publish-subscribe, building on the unified API from JMS 2.0 while updating namespaces to jakarta.* for compatibility with modern Java EE ecosystems.[9] This evolution ensures that PTP functionality remains a core, required feature without altering the fundamental queue semantics from earlier versions.[9]Publish-Subscribe Model
The publish-subscribe (pub/sub) model in Jakarta Messaging enables asynchronous communication where publishers send messages to a topic, and those messages are distributed to multiple active subscribers registered to that topic.[28] This one-to-many distribution supports decoupled interactions in distributed systems, with topics serving as administered objects that encapsulate provider-specific destinations for message routing.[28] Unlike point-to-point messaging, pub/sub allows each message to be consumed by any number of subscribers, or none if no subscribers are active, and topics typically retain messages only until they are delivered to current subscribers.[28][3] Publishers use a TopicPublisher interface, created from a TopicSession, to send messages to a specific topic, while subscribers employ a TopicSubscriber or the unified MessageConsumer interface to receive messages from their subscription.[28] In the unified domain introduced in Jakarta Messaging 2.0, these domain-specific interfaces are optional, allowing a single set of APIs for both point-to-point and pub/sub paradigms.[28] Subscriptions can be non-shared, where each has a single active consumer, or shared, where multiple consumers can attach to the same subscription, with each message delivered to only one of them for load balancing.[28] Durable subscriptions enhance reliability by allowing subscribers to receive messages published while they are offline or inactive, with messages retained for later delivery.[28] These are created using methods likecreateDurableSubscriber or createDurableConsumer, identified by a unique subscription name and, for non-shared cases, a client ID set on the connection.[28][3] Only one active consumer can be associated with a non-shared durable subscription at a time, ensuring ordered delivery upon reconnection.[28]
This model is particularly suited for use cases involving event notifications, such as stock price updates or system alerts, and broadcast updates in distributed applications like content distribution to a sales force.[28][3] Jakarta Messaging 2.0 introduced shared durable subscriptions, allowing multiple consumers to share a durable topic subscription for improved scalability in high-throughput scenarios, with the client ID becoming optional for such configurations.[28] These enhancements, carried forward in version 3.0, mandate full pub/sub support across providers, facilitating more flexible and concurrent message consumption.[28]
Advanced Features
Reliability and Delivery
Jakarta Messaging defines two primary delivery modes to balance reliability and performance: PERSISTENT and NON_PERSISTENT. In PERSISTENT mode, messages are logged to stable storage by the provider, ensuring once-and-only-once delivery semantics even in the event of provider failure, though this incurs higher latency and resource usage. Conversely, NON_PERSISTENT mode provides at-most-once delivery without stable storage, allowing for faster transmission but risking message loss if the provider fails before delivery. The delivery mode is set by the message producer and applies as a default unless overridden per message; if unspecified, the default is PERSISTENT.[29] Acknowledgment modes control how consumers confirm receipt of messages, influencing reliability and potential duplicates. AUTO_ACKNOWLEDGE mode automatically acknowledges messages upon successful return from the receive method or message listener callback, simplifying client code but offering no explicit control. CLIENT_ACKNOWLEDGE requires manual invocation of the message's acknowledge() method, providing finer-grained control where acknowledging one message implicitly confirms all prior messages in the session. DUPS_OK_ACKNOWLEDGE permits lazy acknowledgments, allowing occasional duplicates for improved performance in high-throughput scenarios. SESSION_TRANSACTED ties acknowledgments to transaction boundaries, ensuring atomicity but deferring details to transactional contexts. These modes are configured at the session level.[29][3] Redelivery mechanisms handle failed deliveries by retaining unacknowledged messages for subsequent attempts. If a consumer fails to acknowledge a message—due to exceptions, session closure, or explicit non-acknowledgment—the provider marks it for redelivery, setting the JMSRedelivered header to true and incrementing the JMSXDeliveryCount property to track attempts. This process applies across acknowledgment modes, with unacknowledged messages in CLIENT_ACKNOWLEDGE or AUTO_ACKNOWLEDGE triggering redelivery of prior messages in the session. While the specification mandates redelivery for unacknowledged messages, specifics like maximum attempts or exponential backoff intervals are implementation-dependent to avoid indefinite retries.[29] Message ordering is preserved within the scope of a session to maintain sequence integrity. In the point-to-point (PTP) model using queues, messages sent to a destination are delivered in the order of dispatch to a single consumer, ensuring first-in-first-out (FIFO) behavior for that session. However, the publish-subscribe (pub/sub) model with topics does not guarantee global ordering, as messages fan out to multiple subscribers independently, potentially resulting in varied reception sequences across consumers. Ordering can be influenced by factors like message priority or delivery delay but remains session-bound rather than system-wide.[29] Introduced in Jakarta Messaging 3.0, delayed message delivery enables producers to schedule messages for future availability. Producers specify a delivery delay in milliseconds via setDeliveryDelay(), preventing the message from being dispatched until the interval elapses from the send time. This feature supports time-sensitive workflows, such as deferred notifications, without requiring external scheduling; if unspecified, the delay defaults to zero for immediate delivery. The delay applies per message or as a producer default and integrates with existing delivery modes.[29]Transactions
Jakarta Messaging supports transactions to group message production and consumption operations into atomic units, ensuring that either all operations in a transaction succeed or none are applied. This is achieved through transacted sessions, which are created by invoking thecreateSession(true, Session.AUTO_ACKNOWLEDGE) method on a connection, where the first boolean parameter enables transacted mode.[28] In such sessions, the commit() method finalizes the current transaction by sending produced messages and acknowledging received ones, while rollback() undoes the operations by discarding produced messages and redelivering consumed ones to make them available again.[28] Each transacted session maintains a single ongoing series of transactions, with a new one starting automatically after each commit or rollback.[30]
Local transactions in Jakarta Messaging are confined to a single session and do not involve a two-phase commit protocol, making them suitable for standalone Java SE applications where the application directly manages the transaction lifecycle using commit() and rollback().[28] These transactions ensure atomicity for multiple sends and receives within the session, but all operations must occur on the same session to be grouped together.[28] In Jakarta EE environments, local transaction methods are typically ignored if a container-managed transaction is active, or they may throw a TransactionInProgressException to prevent conflicts with distributed transaction management.[28]
For distributed transactions spanning multiple resources, Jakarta Messaging integrates with the Jakarta Transactions API (JTA) through XA support, which is optional and implemented via XAConnectionFactory, XASession, and the XAResource interface.[28] This allows a Jakarta EE transaction manager to coordinate two-phase commits across messaging resources and other participants, such as databases, enabling atomic operations in enterprise applications.[28] However, XA functionality is not required for all providers, limiting portability in some implementations.[28]
In transacted mode, message delivery semantics are tightly coupled to transaction boundaries: produced messages are not visible to consumers until the transaction commits, and rolled-back transactions trigger redelivery of previously consumed messages as if they had never been received.[28] Delivery delay and time-to-live properties are evaluated at the time of sending, unaffected by subsequent commits.[28]
Transactions in Jakarta Messaging have several limitations, including the single-threaded nature of sessions, which prohibits concurrent access to the same transaction, and restrictions on asynchronous message sends in certain Jakarta EE containers like web or EJB components.[28] Local transactions and client acknowledgments are not permitted when container-managed transactions are active, and temporary queues must be consumed using the same connection that created them.[28] Additionally, durable subscriptions in publish-subscribe domains are not directly encompassed in transactions for operations like creation or unsubscription, though message sends and receives can participate.[31] Jakarta Messaging 2.0 introduced enhancements for JTA integration, including support for simplified APIs like JMSContext that align better with container-managed transactions, improving usability in distributed environments without altering the core XA model.[32]
Configuration and Administration
Connection Factories and JNDI
In Jakarta Messaging, connection factories serve as administered objects that encapsulate configuration parameters necessary for clients to establish connections with a messaging provider. These objects are created and configured by administrators using provider-specific tools and are essential for initializing connections in both point-to-point and publish-subscribe domains.[33][34] Prior to version 2.0, Jakarta Messaging (formerly JMS) relied on domain-specific connection factory types: QueueConnectionFactory for point-to-point messaging, which creates QueueConnection objects, and TopicConnectionFactory for publish-subscribe messaging, which creates TopicConnection objects.[35] With the unification of domains introduced in version 2.0 and carried forward in subsequent releases, including Jakarta Messaging 3.1, the generic ConnectionFactory interface supports both domains, allowing providers to implement a single factory type that can create connections for queues or topics as needed.[36][37] The domain-specific factories remain available for backward compatibility, ensuring seamless migration from earlier implementations. Connection factories are configured with properties such as the provider's server URL or host, authentication credentials (username and password), client ID for durable subscriptions, and reliability-related defaults like delivery mode (persistent or non-persistent) and acknowledgment modes.[38][39] These parameters are set administratively and embedded within the factory object, allowing clients to create connections without hardcoding provider details; for instance, the createConnection(String userName, String password) method can override credentials at runtime if needed.[40] Reliability settings in the factory often include defaults for message persistence to ensure once-and-only-once delivery semantics when combined with appropriate session configurations.[41] Integration with the Java Naming and Directory Interface (JNDI) enables the binding of connection factories and destinations as administered objects within a naming service, promoting loose coupling in enterprise environments. Administrators bind these objects to JNDI names, such as "jms/ConnectionFactory," using provider tools, after which clients obtain an InitialContext and perform lookups via context.lookup("jndiName") to retrieve the factory without knowledge of underlying provider specifics.[42][43] Administered objects must implement the Referenceable interface to support serialization and portability across different JNDI providers.[34] For scenarios involving distributed transactions, XA variants of connection factories provide support for the X/Open XA standard, enabling coordination with the Jakarta Transactions (JTA) API. Examples include XAQueueConnectionFactory for point-to-point domains, which creates XAQueueConnection objects, and XATopicConnectionFactory for publish-subscribe, both extending the base XAConnectionFactory to produce XASession instances that participate in global transactions managed by a transaction manager.[44][45] These XA factories are also bound as administered objects in JNDI, allowing applications in Jakarta EE containers to enlist them transparently in two-phase commit protocols.[46] The use of JNDI with connection factories enhances portability by decoupling application code from vendor-specific implementations, as clients interact solely with standard interfaces regardless of the underlying messaging provider.[47] This abstraction layer ensures that switching providers requires only reconfiguration of administered objects in the naming service, without altering client logic.[34]URI Scheme
RFC 6167 defines thejms: URI scheme to provide a standardized way to identify message destinations, such as queues and topics, promoting interoperability across different messaging providers.[48] This scheme enables applications to reference destinations in a uniform format, independent of provider-specific addressing details. Defined for JMS 1.0, the URI format supports dynamic resolution of destinations through API calls and configuration, with enhanced support for URI-like strings in destination creation methods starting from JMS 2.0 and carried forward into Jakarta Messaging.[48][49]
The basic syntax follows the form jms:<scheme-specific-part>, where the scheme-specific part varies based on the destination type. For point-to-point queues, the variant is jms:queue://<name>, and for publish-subscribe topics, it is jms:topic://<name>. The components include the fixed "jms" scheme, an optional authority section specifying host and port (e.g., for remote resolution), a path segment denoting the destination name, and optional query parameters for additional attributes like message selectors or properties. For example, a simple queue reference might be jms:queue:orders, while a topic with a selector could be jms:topic:news?selector='priority>5'.[48][49]
These URIs are used in several contexts within Jakarta Messaging applications. In API methods, such as Session.createQueue(String) or Session.createTopic(String), the argument can accept a jms: URI string to dynamically create a Queue or Topic object representing the destination.[49] They also appear in JNDI lookups via variants like jms:jndi:<jndi-name>?jndiURL=<context>, allowing resolution through naming services, and in configuration files for declarative setup of messaging resources. This standardization facilitated queue and topic resolution without relying solely on provider-specific strings. Interpretation of such URIs is provider-dependent, as the specification does not mandate a standard format beyond allowing provider-specific strings.[48][49][50]
Jakarta Messaging 3.1 uses the jakarta.jms package namespace for compatibility with the Jakarta EE platform while preserving support for existing URI usage in provider implementations.[51] Query parameters support features like message selectors for filtering, as in the example jms:queue:orders?selector='status=pending', enabling fine-grained control over message routing without altering the base destination.[48]