From 1965f2dd074e8ad5945622d8ca400443f4b53823 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 9 Mar 2018 09:07:38 -0600 Subject: [PATCH] ARTEMIS-1912 big doc refactor - Split protocols into individual chapters - Reorganize summary to flow more logically - Fill in missing parameters in configuration index - Normalize spaces for ordered and unordered lists - Re-wrap lots of text for readability - Fix incorrect XML snippets - Normalize table formatting - Improve internal links with anchors - Update content to reflect new address model - Resized architecture images to avoid excessive white-space - Update some JavaDoc - Update some schema elements - Disambiguate AIO & ASYNCIO where necessary - Use URIs instead of Objects in code examples --- .../api/core/client/ClientSession.java | 40 +- .../schema/artemis-configuration.xsd | 52 +- docs/user-manual/en/SUMMARY.md | 17 +- docs/user-manual/en/address-model.md | 891 ++++++---- docs/user-manual/en/amqp.md | 129 ++ docs/user-manual/en/architecture.md | 153 +- docs/user-manual/en/broker-plugins.md | 242 +-- docs/user-manual/en/client-classpath.md | 17 +- docs/user-manual/en/client-reconnection.md | 137 +- docs/user-manual/en/clusters.md | 842 ++++----- docs/user-manual/en/config-reload.md | 199 ++- docs/user-manual/en/configuration-index.md | 547 +++--- docs/user-manual/en/configuring-transports.md | 707 ++++---- docs/user-manual/en/connection-ttl.md | 44 +- docs/user-manual/en/core-bridges.md | 331 ++-- docs/user-manual/en/core.md | 228 +++ docs/user-manual/en/critical-analysis.md | 8 +- docs/user-manual/en/data-tools.md | 348 ++++ docs/user-manual/en/diverts.md | 6 +- docs/user-manual/en/duplicate-detection.md | 4 +- docs/user-manual/en/embedding-activemq.md | 84 +- docs/user-manual/en/examples.md | 1026 ++++++----- docs/user-manual/en/exclusive-queues.md | 45 +- docs/user-manual/en/filter-expressions.md | 42 +- docs/user-manual/en/flow-control.md | 12 +- docs/user-manual/en/graceful-shutdown.md | 30 +- docs/user-manual/en/ha.md | 334 ++-- docs/user-manual/en/images/architecture1.jpg | Bin 62657 -> 76604 bytes docs/user-manual/en/images/architecture2.jpg | Bin 20043 -> 27525 bytes docs/user-manual/en/images/architecture3.jpg | Bin 14069 -> 15699 bytes .../user-manual/en/intercepting-operations.md | 85 +- docs/user-manual/en/jms-bridge.md | 385 ++--- docs/user-manual/en/jms-core-mapping.md | 20 +- docs/user-manual/en/large-messages.md | 214 +-- docs/user-manual/en/last-value-queues.md | 37 +- docs/user-manual/en/libaio.md | 31 +- docs/user-manual/en/logging.md | 147 +- docs/user-manual/en/management-console.md | 23 +- docs/user-manual/en/management.md | 937 +++++----- docs/user-manual/en/masking-passwords.md | 267 +-- docs/user-manual/en/maven-plugin.md | 50 +- docs/user-manual/en/message-expiry.md | 66 +- docs/user-manual/en/message-grouping.md | 247 ++- docs/user-manual/en/messaging-concepts.md | 399 ++--- docs/user-manual/en/mqtt.md | 137 ++ docs/user-manual/en/network-isolation.md | 90 +- docs/user-manual/en/openwire.md | 112 ++ docs/user-manual/en/paging.md | 202 +-- docs/user-manual/en/perf-tuning.md | 517 +++--- docs/user-manual/en/persistence.md | 353 ++-- docs/user-manual/en/pre-acknowledge.md | 14 +- docs/user-manual/en/preface.md | 54 +- docs/user-manual/en/project-info.md | 19 +- .../en/protocols-interoperability.md | 663 +------- docs/user-manual/en/resource-limits.md | 6 +- docs/user-manual/en/rest.md | 1293 +++++++------- docs/user-manual/en/scheduled-messages.md | 2 +- docs/user-manual/en/security.md | 1509 ++++++++++------- docs/user-manual/en/send-guarantees.md | 18 +- docs/user-manual/en/slow-consumers.md | 2 +- docs/user-manual/en/spring-integration.md | 43 +- docs/user-manual/en/stomp.md | 293 ++++ docs/user-manual/en/syntax.md | 2 +- docs/user-manual/en/tools.md | 216 --- docs/user-manual/en/undelivered-messages.md | 30 +- docs/user-manual/en/unit-testing.md | 42 +- docs/user-manual/en/upgrading.md | 53 +- docs/user-manual/en/using-AMQP.md | 74 - docs/user-manual/en/using-core.md | 212 --- docs/user-manual/en/using-jms.md | 414 ++--- docs/user-manual/en/using-server.md | 240 +-- docs/user-manual/en/versions.md | 8 +- docs/user-manual/en/wildcard-routing.md | 2 +- .../src/main/resources/broker.xml | 13 +- .../src/main/resources/spring-jms-beans.xml | 9 +- 75 files changed, 8241 insertions(+), 7824 deletions(-) create mode 100644 docs/user-manual/en/amqp.md create mode 100644 docs/user-manual/en/core.md create mode 100644 docs/user-manual/en/data-tools.md create mode 100644 docs/user-manual/en/mqtt.md create mode 100644 docs/user-manual/en/openwire.md create mode 100644 docs/user-manual/en/stomp.md delete mode 100644 docs/user-manual/en/tools.md delete mode 100644 docs/user-manual/en/using-AMQP.md delete mode 100644 docs/user-manual/en/using-core.md diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java index ddc8168758a..dbc1e890df4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ClientSession.java @@ -427,7 +427,7 @@ void createTemporaryQueue(SimpleString address, * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable whether the queue is durable or not * @throws ActiveMQException in an exception occurs while creating the queue @@ -440,7 +440,7 @@ void createTemporaryQueue(SimpleString address, * Notice: you will get an exception if the address or the filter doesn't match to an already existent queue * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable if the queue is durable * @throws ActiveMQException in an exception occurs while creating the queue @@ -453,7 +453,7 @@ void createTemporaryQueue(SimpleString address, * Notice: you will get an exception if the address or the filter doesn't match to an already existent queue * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter whether the queue is durable or not * @param durable if the queue is durable @@ -466,7 +466,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates Shared queue. A queue that will exist as long as there are consumers or is durable. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter whether the queue is durable or not * @param durable if the queue is durable @@ -483,7 +483,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param durable whether the queue is durable or not * @throws ActiveMQException in an exception occurs while creating the queue @@ -494,7 +494,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue non-durable queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -504,7 +504,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue non-durable queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -514,7 +514,7 @@ void createSharedQueue(SimpleString address, RoutingType routingType, SimpleStri * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -527,7 +527,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -539,7 +539,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -553,7 +553,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -569,7 +569,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -587,7 +587,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -600,7 +600,7 @@ void createQueue(SimpleString address, RoutingType routingType, SimpleString que * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -616,7 +616,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a non-temporaryqueue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param durable whether the queue is durable or not @@ -634,7 +634,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -644,7 +644,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @throws ActiveMQException in an exception occurs while creating the queue */ @@ -654,7 +654,7 @@ void createQueue(String address, RoutingType routingType, String queueName, Stri * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @param maxConsumers how many concurrent consumers will be allowed on this queue @@ -670,7 +670,7 @@ void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleS * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @throws ActiveMQException in an exception occurs while creating the queue @@ -681,7 +681,7 @@ void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleS * Creates a temporary queue with a filter. * * @param address the queue will be bound to this address - * @param routingType the delivery mode for this queue, MULTICAST or ANYCAST + * @param routingType the routing type for this queue, MULTICAST or ANYCAST * @param queueName the name of the queue * @param filter only messages which match this filter will be put in the queue * @throws ActiveMQException in an exception occurs while creating the queue diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 6a7067141c2..831b4cb8ddd 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -69,9 +69,8 @@ If true then the ActiveMQ Artemis Server will make use of any Protocol Managers that are in available - on the - classpath. If false then only the core protocol will be available, unless in Embedded mode where users - can inject their own Protocol Managers. + on the classpath. If false then only the core protocol will be available, unless in Embedded mode + where users can inject their own Protocol Managers. @@ -216,7 +215,7 @@ - XXX + DEPRECATED: the name of the factory class to use for log delegation @@ -356,7 +355,7 @@ - a list of <class-name/> elements with the names of classes to use for interceptor incoming + a list of <class-name/> elements with the names of classes to use for intercepting incoming remoting packets @@ -365,7 +364,7 @@ - a list of <class-name/> elements with the names of classes to use for interceptor outcoming + a list of <class-name/> elements with the names of classes to use for intercepting outgoing remoting packets @@ -713,10 +712,10 @@ - + - the length of time to wait when opening a new Journal file before timing out and failing + the length of time in seconds to wait when opening a new Journal file before timing out and failing @@ -867,7 +866,7 @@ - regular expression for matching security roles against addresses + pattern for matching security roles against addresses; can use wildards @@ -1075,7 +1074,7 @@ - Wildcard addresses format + parameters to configure wildcard address matching format @@ -1103,9 +1102,20 @@ - - - + + + + a local address to which the datagram socket is bound + + + + + + + a local port to which the datagram socket is bound + + + @@ -1163,7 +1173,6 @@ - @@ -1650,8 +1659,7 @@ DEPRECATED: use message-load-balancing-type instead. Select STRICT to mimic - forward-when-no-consumers=true - and ON_DEMAND to mimic forward-when-no-consumers=false. + forward-when-no-consumers=true and ON_DEMAND to mimic forward-when-no-consumers=false. @@ -1750,7 +1758,7 @@ - XXX -- this is a duplicate... + name of discovery group used by this cluster-connection @@ -2961,7 +2969,7 @@ - + the maximum number of consumers allowed on this queue at any one time @@ -2990,7 +2998,7 @@ - XXX + pattern for matching settings against addresses; can use wildards @@ -3009,7 +3017,7 @@ - how many connections are allowed by the matched entity (-1 means no limit, default is -1) + how many connections are allowed by the matched user (-1 means no limit, default is -1) @@ -3017,7 +3025,7 @@ - how many queues can be created by the matched entity (-1 means no limit, default is -1) + how many queues can be created by the matched user (-1 means no limit, default is -1) @@ -3146,7 +3154,7 @@ - The address name to matches incoming message addresses + The address name to match incoming message addresses diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md index c93f7591295..86273cc82f8 100644 --- a/docs/user-manual/en/SUMMARY.md +++ b/docs/user-manual/en/SUMMARY.md @@ -10,14 +10,18 @@ * [Using the Server](using-server.md) * [Upgrading](upgrading.md) * [Address Model](address-model.md) -* [Using JMS](using-jms.md) -* [Using Core](using-core.md) -* [Using AMQP](using-AMQP.md) +* [Protocols and Interoperability](protocols-interoperability.md) +* [AMQP](amqp.md) +* [MQTT](mqtt.md) +* [STOMP](stomp.md) +* [OpenWire](openwire.md) +* [Core](core.md) * [Mapping JMS Concepts to the Core API](jms-core-mapping.md) +* [Using JMS](using-jms.md) * [The Client Classpath](client-classpath.md) * [Examples](examples.md) * [Routing Messages With Wild Cards](wildcard-routing.md) -* [Understanding the Apache ActiveMQ Artemis Wildcard Syntax](wildcard-syntax.md) +* [Wildcard Syntax](wildcard-syntax.md) * [Filter Expressions](filter-expressions.md) * [Persistence](persistence.md) * [Configuring Transports](configuring-transports.md) @@ -56,14 +60,13 @@ * [Thread management](thread-pooling.md) * [Logging](logging.md) * [REST Interface](rest.md) -* [Embedding Apache ActiveMQ Artemis](embedding-activemq.md) +* [Embedding the Broker](embedding-activemq.md) * [Apache Karaf](karaf.md) * [Apache Tomcat](tomcat.md) * [Spring Integration](spring-integration.md) * [CDI Integration](cdi-integration.md) * [Intercepting Operations](intercepting-operations.md) -* [Protocols and Interoperability](protocols-interoperability.md) -* [Tools](tools.md) +* [Data Tools](data-tools.md) * [Maven Plugin](maven-plugin.md) * [Unit Testing](unit-testing.md) * [Troubleshooting and Performance Tuning](perf-tuning.md) diff --git a/docs/user-manual/en/address-model.md b/docs/user-manual/en/address-model.md index 1673e21e89c..f73adf24a1b 100644 --- a/docs/user-manual/en/address-model.md +++ b/docs/user-manual/en/address-model.md @@ -1,33 +1,66 @@ # Addressing Model -Apache ActiveMQ Artemis has a unique addressing model that is both powerful and flexible and that offers great performance. The addressing model comprises three main concepts: addresses, queues and routing types. +Apache ActiveMQ Artemis has a unique addressing model that is both powerful and +flexible and that offers great performance. The addressing model comprises +three main concepts: **addresses**, **queues** and **routing types**. -An address represents a messaging endpoint. Within the configuration, a typical address is given a unique name, 0 or more queues, and a routing type. +### Address -A queue is associated with an address. There can be multiple queues per address. Once an incoming message is matched to an address, the message will be sent on to one or more of its queues, depending on the routing type configured. Queues can be configured to be automatically created and deleted. +An address represents a messaging endpoint. Within the configuration, a typical +address is given a unique name, 0 or more queues, and a routing type. -A routing type determines how messages are sent to the queues associated with an address. An Apache ActiveMQ Artemis address can be configured with two different routing types. +### Queue + +A queue is associated with an address. There can be multiple queues per +address. Once an incoming message is matched to an address, the message will be +sent on to one or more of its queues, depending on the routing type configured. +Queues can be configured to be automatically created and deleted. + +### Routing Types + +A routing type determines how messages are sent to the queues associated with +an address. An Apache ActiveMQ Artemis address can be configured with two +different routing types. Table 1. Routing Types -| If you want your messages routed to…​ | Use this routing type …​ | -| :----------------------------------------------------------------------: | :---------------------: | -| A single queue within the matching address, in a point-to-point manner. | Anycast | -| Every queue within the matching address, in a publish-subscribe manner. | Multicast | +If you want your messages routed to...|Use this routing type... +---|--- +A single queue within the matching address, in a point-to-point manner.|Anycast +Every queue within the matching address, in a publish-subscribe manner.|Multicast + --------------------------------------------------------------------------------------------- -**Note:** It is possible to define more than one routing type per address, but this typically results in an anti-pattern and is therefore not recommended. If an address does use both routing types, however, and the client does not show a preference for either one, the broker typically defaults to the anycast routing type. -The one exception is when the client uses the MQTT protocol. In that case, the default routing type is multicast. | +**Note:** It is possible to define more than one routing type per address, but +this typically results in an anti-pattern and is therefore not recommended. If +an address does use both routing types, however, and the client does not show a +preference for either one, the broker typically defaults to the anycast routing +type. + +The one exception is when the client uses the MQTT protocol. In that case, the +default routing type is multicast. + +For additional details about these concepts refer to [the core](core.md) chapter. ## Basic Address Configuration -The following examples show how to configure basic point to point and publish subscribe addresses. +The following examples show how to configure basic point to point and publish +subscribe addresses. ### Point-to-Point Messaging -Point-to-point messaging is a common scenario in which a message sent by a producer has only one consumer. AMQP and JMS message producers and consumers can make use of point-to-point messaging queues, for example. Define an anycast routing type for an address so that its queues receive messages in a point-to-point manner. +Point-to-point messaging is a common scenario in which a message sent by a +producer has only one consumer. AMQP and JMS message producers and consumers +can make use of point-to-point messaging queues, for example. Define an anycast +routing type for an address so that its queues receive messages in a +point-to-point manner. -When a message is received on an address using anycast, Apache ActiveMQ Artemis locates the queue associated with the address and routes the message to it. When consumers request to consume from the address, the broker locates the relevant queue and associates this queue with the appropriate consumers. If multiple consumers are connected to the same queue, messages are distributed amongst each consumer equally, providing the consumers are equally able to handle them. +When a message is received on an address using anycast, Apache ActiveMQ Artemis +locates the queue associated with the address and routes the message to it. +When consumers request to consume from the address, the broker locates the +relevant queue and associates this queue with the appropriate consumers. If +multiple consumers are connected to the same queue, messages are distributed +amongst each consumer equally, providing the consumers are equally able to +handle them. ![Point to Point](images/addressing-model-p2p.png) Figure 1. Point to Point Messaging @@ -36,180 +69,207 @@ Figure 1. Point to Point Messaging Open the file `/etc/broker.xml` for editing. -Add an address configuration element and its associated queue if they do not exist already. +Add an address configuration element and its associated queue if they do not +exist already. -**Note** For normal Point to Point semantics, the queue name **MUST** match the address name. +**Note:** For normal Point to Point semantics, the queue name **MUST** match the +address name. ```xml - - - ... -
+ +
- + -
- - +
+ ``` ### Publish-Subscribe Messaging -In a publish-subscribe scenario, messages are sent to every consumer subscribed to an address. JMS topics and MQTT subscriptions are two examples of publish-subscribe messaging. +In a publish-subscribe scenario, messages are sent to every consumer subscribed +to an address. JMS topics and MQTT subscriptions are two examples of +publish-subscribe messaging. -To configure an address with publish-subscribe semantics, create an address with the multicast routing type. +To configure an address with publish-subscribe semantics, create an address +with the multicast routing type. ![Publish Subscribe](images/addressing-model-pubsub.png) Figure 2. Publish-Subscribe #### Using the Multicast Routing Type -Open the file /etc/broker.xml for editing. +Open the file `/etc/broker.xml` for editing. Add an address configuration element with multicast routing type. ```xml - - - ... -
+ +
-
- - +
+ ``` -When clients connect to an address with the multicast element, a subscription queue for the client will be automatically created for the client. It is also possible to pre-configure subscription queues and connect to them directly using the queue's [Fully Qualified Queue names](#fully-qualified-queue-names). +When clients connect to an address with the multicast element, a subscription +queue for the client will be automatically created for the client. It is also +possible to pre-configure subscription queues and connect to them directly +using the queue's [Fully Qualified Queue names](#fully-qualified-queue-names). -Optionally add one or more queue elements to the address and wrap the multicast element around them. This step is typically not needed since the broker will automatically create a queue for each subscription requested by a client. +Optionally add one or more queue elements to the address and wrap the multicast +element around them. This step is typically not needed since the broker will +automatically create a queue for each subscription requested by a client. ```xml - - - ... -
+ +
- - + + -
- - +
+ ``` Figure 3. Point-to-Point with Two Queues ### Point-to-Point Address multiple Queues -It is actually possible to define more than one queue on an address with an anycast routing type. When messages are received on such an address, they are firstly distributed evenly across all the defined queues. Using [Fully Qualified Queue names](#fully-qualified-queue-names), clients are able to select the queue that they would like to subscribe to. Should more than one consumer connect direct to a single queue, Apache ActiveMQ Artemis will take care of distributing messages between them, as in the example above. +It is actually possible to define more than one queue on an address with an +anycast routing type. When messages are received on such an address, they are +firstly distributed evenly across all the defined queues. Using [Fully +Qualified Queue names](#fully-qualified-queue-names), clients are able to +select the queue that they would like to subscribe to. Should more than one +consumer connect direct to a single queue, Apache ActiveMQ Artemis will take +care of distributing messages between them, as in the example above. ![Point to Point](images/addressing-model-p2p2.png) Figure 3. Point-to-Point with Two Queues --------------------------------------------------------------------------------------------- -**Note:** This is how Apache ActiveMQ Artemis handles load balancing of queues across multiple nodes in a cluster. -Configuring a Point-to-Point Address with two queues, open the file /etc/broker.xml for editing. +**Note:** This is how Apache ActiveMQ Artemis handles load balancing of queues +across multiple nodes in a cluster. Configuring a Point-to-Point Address with +two queues, open the file `/etc/broker.xml` for editing. -Add an address configuration with Anycast routing type element and its associated queues. +Add an address configuration with Anycast routing type element and its +associated queues. ```xml - - - ... -
+ +
- - + + -
- - +
+ ``` ### Point-to-Point and Publish-Subscribe Addresses -It is possible to define an address with both point-to-point and publish-subscribe semantics enabled. While not typically recommend, this can be useful when you want, for example, a JMS Queue say orders and a JMS Topic named orders. The different routing types make the addresses appear to be distinct. +It is possible to define an address with both point-to-point and +publish-subscribe semantics enabled. While not typically recommend, this can be +useful when you want, for example, a JMS Queue say orders and a JMS Topic named +orders. The different routing types make the addresses appear to be distinct. -Using an example of JMS Clients, the messages sent by a JMS message producer will be routed using the anycast routing type. Messages sent by a JMS topic producer will use the multicast routing type. In addition when a JMS topic consumer attaches, it will be attached to it’s own subscription queue. JMS queue consumer will be attached to the anycast queue. +Using an example of JMS Clients, the messages sent by a JMS message producer +will be routed using the anycast routing type. Messages sent by a JMS topic +producer will use the multicast routing type. In addition when a JMS topic +consumer attaches, it will be attached to it’s own subscription queue. JMS +queue consumer will be attached to the anycast queue. ![Point to Point](images/addressing-model-p2p-pubsub.png) -Figure 4. [Point-to-Point and Publish-Subscribe - --------------------------------------------------------------------------------------------- -**Note:** The behavior in this scenario is dependent on the protocol being used. For JMS there is a clear distinction between topic and queue producers and consumers, which make the logic straight forward. Other protocols like AMQP do not make this distinction. A message being sent via AMQP will be routed by both anycast and multicast and consumers will default to anycast. For more information, please check the behavior of each protocol in the sections on protocols. - -The XML snippet below is an example of what the configuration for an address using both anycast and multicast would look like in /etc/broker.xml. Note that subscription queues are typically created on demand, so there is no need to list specific queue elements inside the multicast routing type. +Figure 4. Point-to-Point and Publish-Subscribe + +**Note:** The behavior in this scenario is dependent on the protocol being +used. For JMS there is a clear distinction between topic and queue producers +and consumers, which make the logic straight forward. Other protocols like AMQP +do not make this distinction. A message being sent via AMQP will be routed by +both anycast and multicast and consumers will default to anycast. For more +information, please check the behavior of each protocol in the sections on +protocols. + +The XML snippet below is an example of what the configuration for an address +using both anycast and multicast would look like in +`/etc/broker.xml`. Note that subscription queues are typically +created on demand, so there is no need to list specific queue elements inside +the multicast routing type. ```xml - - - ... -
+ +
- + -
- - +
+ ``` ## How to filter messages -Apache ActiveMQ Artemis supports the ability to filter messages using Apache Artemis [Filter Expressions](filter-expressions.md). +Apache ActiveMQ Artemis supports the ability to filter messages using Apache +Artemis [Filter Expressions](filter-expressions.md). Filters can be applied in two places, on a queue and on a consumer. ### Queue Filter -When a filter is applied to a queue, messages are filter before they sent to the queue. To add a queue filter use the -filter element when configuring a queue. Open up the broker.xml and add an address with a queue, using the filter element -to configure a filter on this queue. +When a filter is applied to a queue, messages are filter before they sent to +the queue. To add a queue filter use the filter element when configuring a +queue. Open up `/etc/broker.xml` and add an address with a +queue, using the filter element to configure a filter on this queue. ```xml -
- - - -
+ +
+ + + +
+
``` -The filter defined above ensures that only messages with an attribute "color='red'" is sent to this queue. +The filter defined above ensures that only messages with an attribute +`"color='red'"` is sent to this queue. ### Consumer Filters -Consumer filters are applied after messages have reached a queue and are defined using the appropriate client APIs. The -follow JMS example shows how to consumer filters work. +Consumer filters are applied after messages have reached a queue and are +defined using the appropriate client APIs. The follow JMS example shows how to +consumer filters work. 1. Define an address with a single queue, with no filter applied. ```xml -
- -
+ +
+ +
+
``` ```java - ... - // Send some messages - for (int i = 0; i < 3; i ++) { - TextMessage redMessage = senderSession.createTextMessage("Red"); - redMessage.setStringProperty("color", "red"); - producer.send(redMessage) - - TextMessage greenMessage = senderSession.createTextMessage("Green"); - greenMessage.setStringProperty("color", "green"); - producer.send(greenMessage) - } +... +// Send some messages +for (int i = 0; i < 3; i ++) { + TextMessage redMessage = senderSession.createTextMessage("Red"); + redMessage.setStringProperty("color", "red"); + producer.send(redMessage) + + TextMessage greenMessage = senderSession.createTextMessage("Green"); + greenMessage.setStringProperty("color", "green"); + producer.send(greenMessage) +} ``` At this point the queue would have 6 messages: red,green,red,green,red,green ```java - MessageConsumer redConsumer = redSession.createConsumer(queue, "color='red'"); +MessageConsumer redConsumer = redSession.createConsumer(queue, "color='red'"); ``` -The redConsumer has a filter that only matches "red" messages. The redConsumer will receive 3 messages. +The redConsumer has a filter that only matches "red" messages. The redConsumer +will receive 3 messages. ``` red, red, red @@ -223,92 +283,100 @@ green, green, green ## Automatic Address/Queue Management -You can configure Apache ActiveMQ Artemis to automatically create addresses and queues, and then delete them when they are no longer in use. This saves you from having to preconfigure each address and queue before a client can connect to it. Automatic creation and deletion is configured on a per address basis and is controlled by following: +You can configure Apache ActiveMQ Artemis to automatically create addresses and +queues, and then delete them when they are no longer in use. This saves you +from having to preconfigure each address and queue before a client can connect +to it. Automatic creation and deletion is configured on a per address basis and +is controlled by following: -| Parameter | Description | -|-----------|-------------| -| auto-create-addresses | When set to true, the broker will create the address requested by the client if it does not exist already. The default is true.| -| auto-delete-addresses | When set to true, the broker will be delete any **auto-created** adddress once all of it’s queues have been deleted. The default is true | -|default-address-routing-type | The routing type to use if the client does not specify one. Possible values are MULTICAST and ANYCAST. See earlier in this chapter for more information about routing types. The default value is MULTICAST. | +Parameter|Description +---|--- +`auto-create-addresses`|When set to true, the broker will create the address requested by the client if it does not exist already. The default is `true`. +`auto-delete-addresses`|When set to true, the broker will be delete any **auto-created** adddress once all of it’s queues have been deleted. The default is `true` +`default-address-routing-type`|The routing type to use if the client does not specify one. Possible values are `MULTICAST` and `ANYCAST`. See earlier in this chapter for more information about routing types. The default value is `MULTICAST`. ### Auto Address Creation -- Edit the file `/etc/broker.xml` and add the `auto-create-addresses` element to the `address-setting` you want the broker to automatically create. +- Edit the file `/etc/broker.xml` and add the + `auto-create-addresses` element to the `address-setting` you want the broker + to automatically create. -- (Optional) Add the `address-setting` if it does not exits. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. +- (Optional) Add the `address-setting` if it does not exits. Use the match + parameter and the [wildcard syntax](wildcard-syntax.md) to match more than + one specific address. - Set `auto-create-addresses` to `true` -- (Optional) Assign `MULTICAST` or `ANYCAST` as the default routing type for the address. +- (Optional) Assign `MULTICAST` or `ANYCAST` as the default routing type for + the address. -The example below configures an `address-setting` to be automatically created by the broker. The default routing type to be used if not specified by the client is MULTICAST. Note that wildcard syntax is used. Any address starting with /news/politics/ will be automatically created by the broker. +The example below configures an `address-setting` to be automatically created +by the broker. The default routing type to be used if not specified by the +client is MULTICAST. Note that wildcard syntax is used. Any address starting +with `/news/politics/` will be automatically created by the broker. ```xml - - - ... - - - true - MULTICAST - - - ... - - + + true + MULTICAST + ``` ### Auto Address Deletion -Edit the file `/etc/broker.xml` and add the `auto-delete-addresses` element to the `address-setting` you want the broker to automatically create. +- Edit the file `/etc/broker.xml` and add the + `auto-delete-addresses` element to the `address-setting` you want the broker to + automatically create. -(Optional) Add the `address-setting` if it does not exits. Use the match parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one specific address. +- (Optional) Add the `address-setting` if it does not exits. Use the match + parameter and the [wildcard syntax](wildcard-syntax.md) to match more than one + specific address. -Set `auto-delete-addresses` to `true` +- Set `auto-delete-addresses` to `true` -The example below configures an `address-setting` to be automatically deleted by the broker. Note that wildcard syntax is used. Any address request by the client that starts with `/news/politics/` is configured to be automatically deleted by the broker. +The example below configures an `address-setting` to be automatically deleted +by the broker. Note that wildcard syntax is used. Any address request by the +client that starts with `/news/politics/` is configured to be automatically +deleted by the broker. ```xml - - - ... - - - true - MULTICAST - - - ... - - + + true + MULTICAST + ``` ## "Fully Qualified" Queue Names -Internally the broker maps a client’s request for an address to specific queues. The broker decides on behalf of the client which queues to send messages to or from which queue to receive messages. However, more advanced use cases might require that the client specify a queue directly. In these situations the client and use a fully qualified queue name, by specifying both the address name and the queue name, separated by a ::. +Internally the broker maps a client’s request for an address to specific +queues. The broker decides on behalf of the client which queues to send +messages to or from which queue to receive messages. However, more advanced use +cases might require that the client specify a queue directly. In these +situations the client and use a fully qualified queue name, by specifying both +the address name and the queue name, separated by a ::. -Currently Artemis supports fully qualified queue names on Core, AMQP, JMS, OpenWire, MQTT and Stomp protocols for receiving messages only. +Currently Artemis supports fully qualified queue names on Core, AMQP, JMS, +OpenWire, MQTT and Stomp protocols for receiving messages only. ### Specifying a Fully Qualified Queue Name -In this example, the address foo is configured with two queues q1, q2 as shown in the configuration below. + +In this example, the address foo is configured with two queues q1, q2 as shown +in the configuration below. ```xml - - - ... - -
- - - - -
-
-
-
+ +
+ + + + +
+
``` -In the client code, use both the address name and the queue name when requesting a connection from the broker. Remember to use two colons, ::, to separate the names, as in the example Java code below. +In the client code, use both the address name and the queue name when +requesting a connection from the broker. Remember to use two colons, `::`, to +separate the names, as in the example Java code below. ```java String FQQN = "foo::q1"; @@ -318,302 +386,407 @@ MessageConsumer consumer = session.createConsumer(q1); ## Using Prefixes to Determine Routing Type -Normally, if the broker receives a message sent to a particular address, that has both `ANYCAST` and `MULTICAST` routing types enable, it will route a copy of the message to **one** of the `ANYCAST` queues and to **all** of the `MULTICAST` queues. +Normally, if the broker receives a message sent to a particular address, that +has both `ANYCAST` and `MULTICAST` routing types enable, it will route a copy +of the message to **one** of the `ANYCAST` queues and to **all** of the +`MULTICAST` queues. -However, clients can specify a special prefix when connecting to an address to indicate which kind of routing type to use. The prefixes are custom values that are designated using the anycastPrefix and multicastPrefix parameters within the URL of an acceptor. +However, clients can specify a special prefix when connecting to an address to +indicate which kind of routing type to use. The prefixes are custom values that +are designated using the anycastPrefix and multicastPrefix parameters within +the URL of an acceptor. ### Configuring an Anycast Prefix -In `/etc/broker.xml`, add the `anycastPrefix` to the URL of the desired acceptor. In the example below, the acceptor is configured to use `anycast://` for the `anycastPrefix`. Client code can specify `anycast://foo/` if the client needs to send a message to only one of the `ANYCAST` queues. +In `/etc/broker.xml`, add the `anycastPrefix` to the URL of +the desired acceptor. In the example below, the acceptor is configured to use +`anycast://` for the `anycastPrefix`. Client code can specify `anycast://foo/` +if the client needs to send a message to only one of the `ANYCAST` queues. ```xml - - - ... - - tcp://0.0.0.0:61616?protocols=AMQP;anycastPrefix=anycast:// - - ... - - +tcp://0.0.0.0:61616?protocols=AMQP;anycastPrefix=anycast:// ``` ### Configuring a Multicast Prefix -In `/etc/broker.xml`, add the `multicastPrefix` to the URL of the desired acceptor. In the example below, the acceptor is configured to use `multicast://` for the `multicastPrefix`. Client code can specify `multicast://foo/` if the client needs to send a message to only one of the `MULTICAST` queues. +In `/etc/broker.xml`, add the `multicastPrefix` to the URL of +the desired acceptor. In the example below, the acceptor is configured to use +`multicast://` for the `multicastPrefix`. Client code can specify +`multicast://foo/` if the client needs to send a message to only one of the +`MULTICAST` queues. ```xml - - - ... - - tcp://0.0.0.0:61616?protocols=AMQP;multicastPrefix=multicast:// - - ... - - +tcp://0.0.0.0:61616?protocols=AMQP;multicastPrefix=multicast:// ``` ## Advanced Address Configuration ### Static Subscription Queues -In most cases it’s not necessary to statically configure subscription queues. The relevant protocol managers take care of dynamically creating subscription queues when clients request to subscribe to an address. The type of subscription queue created depends on what properties the client request. For example, durable, non-shared, shared etc. Protocol managers use special queue naming conventions to identify which queues belong to which consumers and users need not worry about the details. - -However, there are scenarios where a user may want to use broker side configuration to statically configure a subscription and later connect to that queue directly using a [Fully Qualified Queue name](#fully-qualified-queue-names). The examples below show how to use broker side configuration to statically configure a queue with publish subscribe behavior for shared, non-shared, durable and non-durable subscription behavior. +In most cases it’s not necessary to statically configure subscription queues. +The relevant protocol managers take care of dynamically creating subscription +queues when clients request to subscribe to an address. The type of +subscription queue created depends on what properties the client request. For +example, durable, non-shared, shared etc. Protocol managers use special queue +naming conventions to identify which queues belong to which consumers and users +need not worry about the details. + +However, there are scenarios where a user may want to use broker side +configuration to statically configure a subscription and later connect to that +queue directly using a [Fully Qualified Queue +name](#fully-qualified-queue-names). The examples below show how to use broker +side configuration to statically configure a queue with publish subscribe +behavior for shared, non-shared, durable and non-durable subscription behavior. #### Shared, Durable Subscription Queue using max-consumers -The default behavior for queues is to not limit the number connected queue consumers. The **max-consumers** parameter of the queue element can be used to limit the number of connected consumers allowed at any one time. +The default behavior for queues is to not limit the number connected queue +consumers. The **max-consumers** parameter of the queue element can be used to +limit the number of connected consumers allowed at any one time. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- - - true - + + + true + -
- - +
+ ``` #### Non-shared, Durable Subscription Queue -The broker can be configured to prevent more than one consumer from connecting to a queue at any one time. The subscriptions to queues configured this way are therefore "non-shared". To do this simply set the **max-consumers** parameter to "1" +The broker can be configured to prevent more than one consumer from connecting +to a queue at any one time. The subscriptions to queues configured this way are +therefore "non-shared". To do this simply set the **max-consumers** parameter +to `1`: ```xml - - - ... -
+ +
- - - true - + + + true + -
- - +
+ ``` #### Non-durable Subscription Queue -Non-durable subscriptions are again usually managed by the relevant protocol manager, by creating and deleting temporary queues. +Non-durable subscriptions are again usually managed by the relevant protocol +manager, by creating and deleting temporary queues. -If a user requires to pre-create a queue that behaves like a non-durable subscription queue the **purge-on-no-consumers** flag can be enabled on the queue. When **purge-on-no-consumers** is set to **true**. The queue will not start receiving messages until a consumer is attached. When the last consumer is detached from the queue. The queue is purged (it's messages are removed) and will not receive any more messages until a new consumer is attached. +If a user requires to pre-create a queue that behaves like a non-durable +subscription queue the **purge-on-no-consumers** flag can be enabled on the +queue. When **purge-on-no-consumers** is set to **true**. The queue will not +start receiving messages until a consumer is attached. When the last consumer +is detached from the queue. The queue is purged (it's messages are removed) +and will not receive any more messages until a new consumer is attached. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- + -
- - +
+ ``` #### Exclusive Consumer Queue -If a user requires to statically configure a queue that routes exclusively to one active consumer the **exclusive** flag can be enabled on the queue. +If a user requires to statically configure a queue that routes exclusively to +one active consumer the **exclusive** flag can be enabled on the queue. -When **exclusive** is set to **true** the queue will route messages to the a single active consumer. When the active consumer that is being routed to is detached from the queue, if another active consumer exist, one will be chosen and routing will now be exclusive to it. +When **exclusive** is set to **true** the queue will route messages to the a +single active consumer. When the active consumer that is being routed to is +detached from the queue, if another active consumer exist, one will be chosen +and routing will now be exclusive to it. See [Exclusive Queue](exclusive-queues.md) for further information. Open the file `/etc/broker.xml` for editing. ```xml - - - ... -
+ +
- + -
- - +
+ ``` ## Protocol Managers -A "protocol manager" maps protocol-specific concepts down to the core addressing model (using addresses, queues and routing types). For example, when a client sends a MQTT subscription packet with the addresses: +A "protocol manager" maps protocol-specific concepts down to the core +addressing model (using addresses, queues and routing types). For example, when +a client sends a MQTT subscription packet with the addresses: ``` /house/room1/lights /house/room2/lights ``` -The MQTT protocol manager understands that the two addresses require `MULTICAST` semantics. The protocol manager will therefore first look to ensure that `MULTICAST` is enabled for both addresses. If not, it will attempt to dynamically create them. If successful, the protocol manager will then create special subscription queues with special names, for each subscription requested by the client. +The MQTT protocol manager understands that the two addresses require +`MULTICAST` semantics. The protocol manager will therefore first look to ensure +that `MULTICAST` is enabled for both addresses. If not, it will attempt to +dynamically create them. If successful, the protocol manager will then create +special subscription queues with special names, for each subscription requested +by the client. -The special name allows the protocol manager to quickly identify the required client subscription queues should the client disconnect and reconnect at a later date. If the subscription is temporary the protocol manager will delete the queue once the client disconnects. +The special name allows the protocol manager to quickly identify the required +client subscription queues should the client disconnect and reconnect at a +later date. If the subscription is temporary the protocol manager will delete +the queue once the client disconnects. -When a client requests to subscribe to a point to point address. The protocol manager will look up the queue associated with the point to point address. This queue should have the same name as the addresss. +When a client requests to subscribe to a point to point address. The protocol +manager will look up the queue associated with the point to point address. +This queue should have the same name as the addresss. -**Note:** If the queue is auto created, it will be auto deleted once there are no consumers and no messages in it. For more information on auto create see the next section [Configuring Addresses and Queues via Address Settings](#configuring-addresses-and-queues-via-address-settings) +**Note:** If the queue is auto created, it will be auto deleted once there are +no consumers and no messages in it. For more information on auto create see +the next section [Configuring Addresses and Queues via Address +Settings](#configuring-addresses-and-queues-via-address-settings) ## Configuring Addresses and Queues via Address Settings -There are some attributes that are defined against an address wildcard -rather than a specific address/queue. Here an example of an `address-setting` -entry that would be found in the `broker.xml` file. +There are some attributes that are defined against an address wildcard rather +than a specific address/queue. Here an example of an `address-setting` entry +that would be found in the `broker.xml` file. ```xml DLA - 3 - 5000 ExpiryQueue - true + 123 + 5000 + 1.0 + 10000 + 3 100000 + -1 20000 + + PAGE + + true + true + 0 true - PAGE -1 NOTIFY 5 + true + true + true + true + true + true + OFF + true + true + OFF + 200 + false + -1 + + ``` -The idea with address settings, is you can provide a block of settings -which will be applied against any addresses that match the string in the -`match` attribute. In the above example the settings would only be -applied to the address "order.foo" address but you can also use wildcards -to apply settings. See: [The chapter on the wild card syntax](wildcard-syntax.md). - -For example, if you used the `match` string `queue.#` the settings -would be applied to all addresses which start with `queue.` - -The meaning of the specific settings are explained fully throughout the -user manual, however here is a brief description with a link to the -appropriate chapter if available. +The idea with address settings, is you can provide a block of settings which +will be applied against any addresses that match the string in the `match` +attribute. In the above example the settings would only be applied to the +address "order.foo" address but you can also use +[wildcards](wildcard-syntax.md) to apply settings. + +For example, if you used the `match` string `queue.#` the settings would be +applied to all addresses which start with `queue.` + +The meaning of the specific settings are explained fully throughout the user +manual, however here is a brief description with a link to the appropriate +chapter if available. + +`dead-letter-address` is the address to which messages are sent when they +exceed `max-delivery-attempts`. If no address is defined here then such +messages will simply be discarded. Read more about [undelivered +messages](undelivered-messages.md#configuring-dead-letter-addresses). + +`expiry-address` defines where to send a message that has expired. If no +address is defined here then such messages will simply be discarded. Read more +about [message expiry](message-expiry.md#configuring-expiry-addresses). + +`expiry-delay` defines the expiration time that will be used for messages which +are using the default expiration time (i.e. 0). For example, if `expiry-delay` +is set to "10" and a message which is using the default expiration time (i.e. +0) arrives then its expiration time of "0" will be changed to "10." However, if +a message which is using an expiration time of "20" arrives then its expiration +time will remain unchanged. Setting `expiry-delay` to "-1" will disable this +feature. The default is "-1". Read more about [message +expiry](message-expiry.md#configuring-expiry-addresses). `max-delivery-attempts` defines how many time a cancelled message can be -redelivered before sending to the `dead-letter-address`. A full -explanation can be found [here](undelivered-messages.md#configuring-dead-letter-addresses). - -`redelivery-delay` defines how long to wait before attempting redelivery -of a cancelled message. see [here](undelivered-messages.md#configuring-delayed-redelivery). - -`expiry-address` defines where to send a message that has expired. see -[here](message-expiry.md#configuring-expiry-addresses). - -`expiry-delay` defines the expiration time that will be used for -messages which are using the default expiration time (i.e. 0). For -example, if `expiry-delay` is set to "10" and a message which is using -the default expiration time (i.e. 0) arrives then its expiration time of -"0" will be changed to "10." However, if a message which is using an -expiration time of "20" arrives then its expiration time will remain -unchanged. Setting `expiry-delay` to "-1" will disable this feature. The -default is "-1". - -`last-value-queue` defines whether a queue only uses last values or not. -see [here](last-value-queues.md). - -`max-size-bytes` and `page-size-bytes` are used to set paging on an -address. This is explained [here](paging.md#configuration). - -`redistribution-delay` defines how long to wait when the last consumer -is closed on a queue before redistributing any messages. see -[here](clusters.md#message-redistribution). - -`send-to-dla-on-no-route`. If a message is sent to an address, but the -server does not route it to any queues, for example, there might be no -queues bound to that address, or none of the queues have filters that -match, then normally that message would be discarded. However if this -parameter is set to true for that address, if the message is not routed -to any queues it will instead be sent to the dead letter address (DLA) -for that address, if it exists. - -`address-full-policy`. This attribute can have one of the following -values: PAGE, DROP, FAIL or BLOCK and determines what happens when an -address where `max-size-bytes` is specified becomes full. The default -value is PAGE. If the value is PAGE then further messages will be paged -to disk. If the value is DROP then further messages will be silently -dropped. If the value is FAIL then further messages will be dropped and -an exception will be thrown on the client-side. If the value is BLOCK -then client message producers will block when they try and send further -messages. See the following chapters for more info [Flow Control](flow-control.md), [Paging](paging.md). - -`slow-consumer-threshold`. The minimum rate of message consumption -allowed before a consumer is considered "slow." Measured in -messages-per-second. Default is -1 (i.e. disabled); any other valid -value must be greater than 0. - -`slow-consumer-policy`. What should happen when a slow consumer is -detected. `KILL` will kill the consumer's connection (which will -obviously impact any other client threads using that same connection). -`NOTIFY` will send a CONSUMER\_SLOW management notification which an -application could receive and take action with. See [slow consumers](slow-consumers.md) for more details -on this notification. +redelivered before sending to the `dead-letter-address`. Read more about +[undelivered +messages](undelivered-messages.md#configuring-dead-letter-addresses). + +`redelivery-delay` defines how long to wait before attempting redelivery of a +cancelled message. Default is `0`. Read more about [undelivered +messages](undelivered-messages.md#configuring-delayed-redelivery). + +`redelivery-delay-multiplier` defines the number by which the +`redelivery-delay` will be multiplied on each subsequent redelivery attempt. +Default is `1.0`. Read more about [undelivered +messages](undelivered-messages.md#configuring-delayed-redelivery). + +`max-size-bytes`, `page-size-bytes`, & `page-max-cache-size` are used to +configure paging on an address. This is explained +[here](paging.md#configuration). + +`max-size-bytes-reject-threshold` is used with the address full `BLOCK` policy, +the maximum size (in bytes) an address can reach before messages start getting +rejected. Works in combination with `max-size-bytes` **for AMQP clients only**. +Default is `-1` (i.e. no limit). + +`address-full-policy`. This attribute can have one of the following values: +`PAGE`, `DROP`, `FAIL` or `BLOCK` and determines what happens when an address +where `max-size-bytes` is specified becomes full. The default value is `PAGE`. +If the value is `PAGE` then further messages will be paged to disk. If the +value is `DROP` then further messages will be silently dropped. If the value is +`FAIL` then further messages will be dropped and an exception will be thrown on +the client-side. If the value is `BLOCK` then client message producers will +block when they try and send further messages. See the [Flow +Control](flow-control.md) and [Paging](paging.md) chapters for more info. + +`message-counter-history-day-limit` is the number of days to keep message +counter history for this address assuming that `message-counter-enabled` is +`true`. Default is `0`. + +`last-value-queue` is **deprecated**. See `default-last-value-queue`. It +defines whether a queue only uses last values or not. Default is `false`. Read +more about [last value queues](last-value-queues.md). + +`default-last-value-queue` defines whether a queue only uses last values or +not. Default is `false`. This value can be overridden at the queue level using +the `last-value` boolean. Read more about [last value +queues](last-value-queues.md). + +`default-exclusive-queue` defines whether a queue will serve only a single +consumer. Default is `false`. This value can be overridden at the queue level +using the `exclusive` boolean. Read more about [exclusive +queues](exclusive-queues.md). + +`redistribution-delay` defines how long to wait when the last consumer is +closed on a queue before redistributing any messages. Read more about +[clusters](clusters.md#message-redistribution). + +`send-to-dla-on-no-route`. If a message is sent to an address, but the server +does not route it to any queues (e.g. there might be no queues bound to that +address, or none of the queues have filters that match) then normally that +message would be discarded. However, if this parameter is `true` then such a +message will instead be sent to the `dead-letter-address` (DLA) for that +address, if it exists. + +`slow-consumer-threshold`. The minimum rate of message consumption allowed +before a consumer is considered "slow." Measured in messages-per-second. +Default is `-1` (i.e. disabled); any other valid value must be greater than 0. +Read more about [slow consumers](slow-consumers.md). + +`slow-consumer-policy`. What should happen when a slow consumer is detected. +`KILL` will kill the consumer's connection (which will obviously impact any +other client threads using that same connection). `NOTIFY` will send a +CONSUMER\_SLOW management notification which an application could receive and +take action with. Read more about [slow consumers](slow-consumers.md). `slow-consumer-check-period`. How often to check for slow consumers on a -particular queue. Measured in seconds. Default is 5. See [slow consumers](slow-consumers.md) -for more information about slow consumer detection. - -`auto-create-jms-queues`. Whether or not the broker should automatically -create a JMS queue when a JMS message is sent to a queue whose name fits -the address `match` (remember, a JMS queue is just a core queue which has -the same address and queue name) or a JMS consumer tries to connect to a -queue whose name fits the address `match`. Queues which are auto-created -are durable, non-temporary, and non-transient. Default is `true`. This is -_DEPRECATED_. See `auto-create-queues`. - -`auto-delete-jms-queues`. Whether or not the broker should automatically -delete auto-created JMS queues when they have both 0 consumers and 0 messages. -Default is `true`. This is _DEPRECATED_. See `auto-delete-queues`. - -`auto-create-jms-topics`. Whether or not the broker should automatically -create a JMS topic when a JMS message is sent to a topic whose name fits -the address `match` (remember, a JMS topic is just a core address which has -one or more core queues mapped to it) or a JMS consumer tries to subscribe -to a topic whose name fits the address `match`. Default is `true`. This is -_DEPRECATED_. See `auto-create-addresses`. - -`auto-delete-jms-topics`. Whether or not the broker should automatically -delete auto-created JMS topics once the last subscription on the topic has -been closed. Default is `true`. This is _DEPRECATED_. See `auto-delete-addresses`. - -`auto-create-queues`. Whether or not the broker should automatically -create a queue when a message is sent or a consumer tries to connect to a -queue whose name fits the address `match`. Queues which are auto-created -are durable, non-temporary, and non-transient. Default is `true`. - -`auto-delete-queues`. Whether or not the broker should automatically -delete auto-created queues when they have both 0 consumers and 0 messages. -Default is `true`. - -`config-delete-queues`. How the broker should handle queues deleted -on config reload, by delete policy: `OFF` or `FORCE`. -See [config-reload](config-reload.md) for more details. -Default is `OFF`. - -`auto-create-addresses`. Whether or not the broker should automatically -create an address when a message is sent to or a consumer tries to consume -from a queue which is mapped to an address whose name fits the address `match`. -Default is `true`. - -`auto-delete-addresses`. Whether or not the broker should automatically -delete auto-created addresses once the address no longer has any queues. +particular queue. Measured in *seconds*. Default is `5`. Read more about [slow +consumers](slow-consumers.md). + +`auto-create-jms-queues` is **deprecated**. See `auto-create-queues`. Whether +or not the broker should automatically create a JMS queue when a JMS message is +sent to a queue whose name fits the address `match` (remember, a JMS queue is +just a core queue which has the same address and queue name) or a JMS consumer +tries to connect to a queue whose name fits the address `match`. Queues which +are auto-created are durable, non-temporary, and non-transient. Default is +`true`. + +`auto-delete-jms-queues` is **deprecated**. See `auto-delete-queues`. Whether +or not the broker should automatically delete auto-created JMS queues when they +have both 0 consumers and 0 messages. Default is `true`. + +`auto-create-jms-topics` is **deprecated**. See `auto-create-addresses`. +Whether or not the broker should automatically create a JMS topic when a JMS +message is sent to a topic whose name fits the address `match` (remember, a JMS +topic is just a core address which has one or more core queues mapped to it) or +a JMS consumer tries to subscribe to a topic whose name fits the address +`match`. Default is `true`. + +`auto-delete-jms-topics` is **deprecated**. See `auto-delete-addresses`. +Whether or not the broker should automatically delete auto-created JMS topics +once the last subscription on the topic has been closed. Default is `true`. + +`auto-create-queues`. Whether or not the broker should automatically create a +queue when a message is sent or a consumer tries to connect to a queue whose +name fits the address `match`. Queues which are auto-created are durable, +non-temporary, and non-transient. Default is `true`. + +`auto-delete-queues`. Whether or not the broker should automatically delete +auto-created queues when they have both 0 consumers and 0 messages. Default is +`true`. + +`config-delete-queues`. How the broker should handle queues deleted on config +reload, by delete policy: `OFF` or `FORCE`. Default is `OFF`. Read more about +[configuration reload](config-reload.md). + +`auto-create-addresses`. Whether or not the broker should automatically create +an address when a message is sent to or a consumer tries to consume from a +queue which is mapped to an address whose name fits the address `match`. Default is `true`. -`config-delete-addresses`. How the broker should handle addresses deleted -on config reload, by delete policy: `OFF` or `FORCE`. -See [config-reload](config-reload.md) for more details. -Default is `OFF`. \ No newline at end of file +`auto-delete-addresses`. Whether or not the broker should automatically delete +auto-created addresses once the address no longer has any queues. Default is +`true`. + +`config-delete-addresses`. How the broker should handle addresses deleted on +config reload, by delete policy: `OFF` or `FORCE`. Default is `OFF`. Read more +about [configuration reload](config-reload.md). + +`management-browse-page-size` is the number of messages a management resource +can browse. This is relevant for the "browse" management method exposed on the +queue control. Default is `200`. + +`default-purge-on-no-consumers` defines a queue's default +`purge-on-no-consumers` setting if none is provided on the queue itself. +Default is `false`. This value can be overridden at the queue level using the +`purge-on-no-consumers` boolean. Read more about [this +functionality](#non-durable-subscription-queue). + +`default-max-consumers` defines a queue's default `max-consumers` setting if +none is provided on the queue itself. Default is `-1` (i.e. no limit). This +value can be overridden at the queue level using the `max-consumers` boolean. +Read more about [this +functionality](#shared-durable-subscription-queue-using-max-consumers). + +`default-queue-routing-type` defines the routing-type for an auto-created queue +if the broker is unable to determine the routing-type based on the client +and/or protocol semantics. Default is `MULTICAST`. Read more about [routing +types](#routing-type). + +`default-address-routing-type` defines the routing-type for an auto-created +address if the broker is unable to determine the routing-type based on the +client and/or protocol semantics. Default is `MULTICAST`. Read more about +[routing types](#routing-type). diff --git a/docs/user-manual/en/amqp.md b/docs/user-manual/en/amqp.md new file mode 100644 index 00000000000..a201fbd998b --- /dev/null +++ b/docs/user-manual/en/amqp.md @@ -0,0 +1,129 @@ +# AMQP + +Apache ActiveMQ Artemis supports the [AMQP +1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) +specification. By default there are `acceptor` elements configured to accept +AMQP connections on ports `61616` and `5672`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for AMQP. + +You can use *any* AMQP 1.0 compatible clients. + +A short list includes: + +- [qpid clients](https://qpid.apache.org/download.html) +- [.NET Clients](https://blogs.apache.org/activemq/entry/using-net-libraries-with-activemq) +- [Javascript NodeJS](https://github.com/noodlefrenzy/node-amqp10) +- [Java Script RHEA](https://github.com/grs/rhea) +- ... and many others. + +## Examples + +We have a few examples as part of the Artemis distribution: + +- .NET: + - ./examples/protocols/amqp/dotnet +- ProtonCPP + - ./examples/protocols/amqp/proton-cpp + - ./examples/protocols/amqp/proton-clustered-cpp +- Ruby + - ./examples/protocols/amqp/proton-ruby +- Java (Using the qpid JMS Client) + - ./examples/protocols/amqp/queue +- Interceptors + - ./examples/features/standard/interceptor-amqp + - ./examples/features/standard/broker-plugin + +## Message Conversions + +The broker will not perform any message conversion to any other protocols when +sending AMQP and receiving AMQP. + +However if you intend your message to be received by an AMQP JMS Client, you +must follow the [JMS Mapping +Conventions](https://www.oasis-open.org/committees/download.php/53086/amqp-bindmap-jms-v1.0-wd05.pdf). +If you send a body type that is not recognized by this specification the +conversion between AMQP and any other protocol will make it a Binary Message. +Make sure you follow these conventions if you intend to cross protocols or +languages. Especially on the message body. + +A compatibility setting allows aligning the naming convention of AMQP queues +(JMS Durable and Shared Subscriptions) with CORE. For backwards compatibility +reasons, you need to explicitly enable this via broker configuration: + +- `amqp-use-core-subscription-naming` + - `true` - use queue naming convention that is aligned with CORE. + - `false` (default) - use older naming convention. + +## Intercepting and changing messages + +We don't recommend changing messages at the server's side for a few reasons: + +- AMQP messages are meant to be immutable +- The message won't be the original message the user sent +- AMQP has the possibility of signing messages. The signature would be broken. +- For performance reasons. We try not to re-encode (or even decode) messages. + +If regardless these recommendations you still need and want to intercept and +change AMQP messages, look at the aforementioned interceptor examples. + +## AMQP and security + +The Apache ActiveMQ Artemis Server accepts the PLAIN, ANONYMOUS, and GSSAPI +SASL mechanism. These are implemented on the broker's [security](security.md) +infrastructure. + +## AMQP and destinations + +If an AMQP Link is dynamic then a temporary queue will be created and either +the remote source or remote target address will be set to the name of the +temporary queue. If the Link is not dynamic then the the address of the remote +target or source will used for the queue. If this does not exist then it will +be auto-created if the settings allow. + +## AMQP and Multicast Addresses (Topics) + +Although AMQP has no notion of "topics" it is still possible to treat AMQP +consumers or receivers as subscriptions rather than just consumers on a queue. +By default any receiving link that attaches to an address that has only +`multicast` enabled will be treated as a subscription and a corresponding +subscription queue will be created. If the Terminus Durability is either +`UNSETTLED_STATE` or `CONFIGURATION` then the queue will be made durable +(similar to a JMS durable subscription) and given a name made up from the +container id and the link name, something like `my-container-id:my-link-name`. +If the Terminus Durability is configured as `NONE` then a volatile `multicast` +queue will be created. + +## AMQP and Coordinations - Handling Transactions + +An AMQP links target can also be a Coordinator. A Coordinator is used to handle +transactions. If a coordinator is used then the underlying server session will +be transacted and will be either rolled back or committed via the coordinator. + +> **Note:** +> +> AMQP allows the use of multiple transactions per session, +> `amqp:multi-txns-per-ssn`, however in this version of Apache ActiveMQ Artemis +> will only support single transactions per session. + +## AMQP scheduling message delivery + +An AMQP message can provide scheduling information that controls the time in +the future when the message will be delivered at the earliest. This +information is provided by adding a message annotation to the sent message. + +There are two different message annotations that can be used to schedule a +message for later delivery: + +- `x-opt-delivery-time` + The specified value must be a positive long corresponding to the time the + message should be made available for delivery (in milliseconds). + +- `x-opt-delivery-delay` + The specified value must be a positive long corresponding to the amount of + milliseconds after the broker receives the given message before it should be + made available for delivery. + +If both annotations are present in the same message then the broker will prefer +the more specific `x-opt-delivery-time` value. diff --git a/docs/user-manual/en/architecture.md b/docs/user-manual/en/architecture.md index 80d70b2bf5e..6a73a64cab9 100644 --- a/docs/user-manual/en/architecture.md +++ b/docs/user-manual/en/architecture.md @@ -1,125 +1,126 @@ # Core Architecture -Apache ActiveMQ Artemis core is designed simply as set of Plain Old Java Objects -(POJOs) - we hope you like its clean-cut design. +Apache ActiveMQ Artemis core is designed simply as set of Plain Old Java +Objects (POJOs) - we hope you like its clean-cut design. -Each Apache ActiveMQ Artemis server has its own ultra high performance persistent -journal, which it uses for message and other persistence. +Each Apache ActiveMQ Artemis server has its own ultra high performance +persistent journal, which it uses for message and other persistence. Using a high performance journal allows outrageous persistence message -performance, something not achievable when using a relational database -for persistence. +performance, something not achievable when using a relational database for +persistence (although JDBC is still an option if necessary). -Apache ActiveMQ Artemis clients, potentially on different physical machines interact -with the Apache ActiveMQ Artemis server. Apache ActiveMQ Artemis currently provides two APIs for -messaging at the client side: +Apache ActiveMQ Artemis clients, potentially on different physical machines, +interact with the Apache ActiveMQ Artemis broker. Apache ActiveMQ Artemis +currently ships two API implementations for messaging at the client side: -1. Core client API. This is a simple intuitive Java API that is aligned with the Artemis internal Core. Allowing more - control of broker objects, like for example, direct creation of addresses and queues. The Core API also offers a - full set of messaging functionality without some of the complexities of JMS. +1. Core client API. This is a simple intuitive Java API that is aligned with + the Artemis internal Core. Allowing more control of broker objects (e.g + direct creation of addresses and queues). The Core API also offers a full set + of messaging functionality without some of the complexities of JMS. -2. JMS client API. The standard JMS API is available at the client side. +2. JMS 2.0 client API. The standard JMS API is available at the client side. -Apache ActiveMQ Artemis also provides different protocol implementations on the server so you can use respective clients for these protocols: - -1. AMQP -2. OpenWire -3. MQTT -4. STOMP -5. HornetQ (for use with HornetQ clients). -6. CORE (Artemis CORE protocol) +Apache ActiveMQ Artemis also provides different protocol implementations on the +server so you can use respective clients for these protocols: +- AMQP +- OpenWire +- MQTT +- STOMP +- HornetQ (for use with HornetQ clients). +- Core (Artemis CORE protocol) JMS semantics are implemented by a JMS facade layer on the client side. -The Apache ActiveMQ Artemis server does not speak JMS and in fact does not know -anything about JMS, it is a protocol agnostic messaging server designed -to be used with multiple different protocols. +The Apache ActiveMQ Artemis broker does not speak JMS and in fact does not know +anything about JMS, it is a protocol agnostic messaging server designed to be +used with multiple different protocols. -When a user uses the JMS API on the client side, all JMS interactions -are translated into operations on the Apache ActiveMQ Artemis core client API before -being transferred over the wire using the Apache ActiveMQ Artemis wire format. +When a user uses the JMS API on the client side, all JMS interactions are +translated into operations on the Apache ActiveMQ Artemis core client API +before being transferred over the wire using the core protocol. -The server always just deals with core API interactions. +The broker always just deals with core API interactions. A schematic illustrating this relationship is shown in figure 3.1 below: ![ActiveMQ Artemis architecture1](images/architecture1.jpg) -Figure 3.1 shows two user applications interacting with an Apache ActiveMQ Artemis -server. User Application 1 is using the JMS API, while User Application +Figure 3.1 shows two user applications interacting with an Apache ActiveMQ +Artemis server. User Application 1 is using the JMS API, while User Application 2 is using the core client API directly. -You can see from the diagram that the JMS API is implemented by a thin -facade layer on the client side. +You can see from the diagram that the JMS API is implemented by a thin facade +layer on the client side. ## Stand-alone Broker The normal stand-alone messaging broker configuration comprises a core -messaging broker and a number of protocol managers that provide support for -the various protocol mentioned earlier. Protocol managers are pluggable -if you +messaging broker and a number of protocol managers that provide support for the +various protocol mentioned earlier. -The stand-alone broker configuration uses [Airline](https://github.com/airlift/airline) -for bootstrapping the Broker. +The stand-alone broker configuration uses +[Airline](https://github.com/airlift/airline) for bootstrapping the Broker. The stand-alone broker architecture is shown in figure 3.3 below: ![ActiveMQ Artemis architecture3](images/architecture3.jpg) -For more information on server configuration files see [Server Configuration](configuration-index.md) +For more information on server configuration files see [Server +Configuration](configuration-index.md) ## Embedded Broker -Apache ActiveMQ Artemis core is designed as a set of simple POJOs so if you have an -application that requires messaging functionality internally but you +Apache ActiveMQ Artemis core is designed as a set of simple POJOs so if you +have an application that requires messaging functionality internally but you don't want to expose that as an Apache ActiveMQ Artemis broker you can directly -instantiate and embed Apache ActiveMQ Artemis brokers in your own application. +instantiate and embed brokers in your own application. -For more information on embedding Apache ActiveMQ Artemis, see [Embedding Apache ActiveMQ Artemis](embedding-activemq.md). +Read more about [embedding Apache ActiveMQ Artemis](embedding-activemq.md). ## Integrated with a Java EE application server -Apache ActiveMQ Artemis provides its own fully functional Java Connector Architecture -(JCA) adaptor which enables it to be integrated easily into any Java EE -compliant application server or servlet engine. +Apache ActiveMQ Artemis provides its own fully functional Java Connector +Architecture (JCA) adaptor which enables it to be integrated easily into any +Java EE compliant application server or servlet engine. Java EE application servers provide Message Driven Beans (MDBs), which are a -special type of Enterprise Java Beans (EJBs) that can process messages -from sources such as JMS systems or mail systems. +special type of Enterprise Java Beans (EJBs) that can process messages from +sources such as JMS systems or mail systems. Probably the most common use of an MDB is to consume messages from a JMS messaging system. According to the Java EE specification, a Java EE application server uses a JCA -adapter to integrate with a JMS messaging system so it can consume -messages for MDBs. - -However, the JCA adapter is not only used by the Java EE application server -for *consuming* messages via MDBs, it is also used when sending message -to the JMS messaging system e.g. from inside an EJB or servlet. - -When integrating with a JMS messaging system from inside a Java EE -application server it is always recommended that this is done via a JCA -adaptor. In fact, communicating with a JMS messaging system directly, -without using JCA would be illegal according to the Java EE specification. - -The application server's JCA service provides extra functionality such -as connection pooling and automatic transaction enlistment, which are -desirable when using messaging, say, from inside an EJB. It is possible -to talk to a JMS messaging system directly from an EJB, MDB or servlet -without going through a JCA adapter, but this is not recommended since -you will not be able to take advantage of the JCA features, such as -caching of JMS sessions, which can result in poor performance. - -Figure 3.2 below shows a Java EE application server integrating with a -Apache ActiveMQ Artemis server via the Apache ActiveMQ Artemis JCA adaptor. Note that all -communication between EJB sessions or entity beans and Message Driven -beans go through the adaptor and not directly to Apache ActiveMQ Artemis. - -The large arrow with the prohibited sign shows an EJB session bean -talking directly to the Apache ActiveMQ Artemis server. This is not recommended as -you'll most likely end up creating a new connection and session every -time you want to interact from the EJB, which is an anti-pattern. +adapter to integrate with a JMS messaging system so it can consume messages for +MDBs. + +However, the JCA adapter is not only used by the Java EE application server for +*consuming* messages via MDBs, it is also used when sending message to the JMS +messaging system e.g. from inside an EJB or servlet. + +When integrating with a JMS messaging system from inside a Java EE application +server it is always recommended that this is done via a JCA adaptor. In fact, +communicating with a JMS messaging system directly, without using JCA would be +illegal according to the Java EE specification. + +The application server's JCA service provides extra functionality such as +connection pooling and automatic transaction enlistment, which are desirable +when using messaging, say, from inside an EJB. It is possible to talk to a JMS +messaging system directly from an EJB, MDB or servlet without going through a +JCA adapter, but this is not recommended since you will not be able to take +advantage of the JCA features, such as caching of JMS sessions, which can +result in poor performance. + +Figure 3.2 below shows a Java EE application server integrating with a Apache +ActiveMQ Artemis server via the Apache ActiveMQ Artemis JCA adaptor. Note that +all communication between EJB sessions or entity beans and Message Driven beans +go through the adaptor and not directly to Apache ActiveMQ Artemis. + +The large arrow with the prohibited sign shows an EJB session bean talking +directly to the Apache ActiveMQ Artemis server. This is not recommended as +you'll most likely end up creating a new connection and session every time you +want to interact from the EJB, which is an anti-pattern. ![ActiveMQ Artemis architecture2](images/architecture2.jpg) diff --git a/docs/user-manual/en/broker-plugins.md b/docs/user-manual/en/broker-plugins.md index 6fd03a810ba..442339a985c 100644 --- a/docs/user-manual/en/broker-plugins.md +++ b/docs/user-manual/en/broker-plugins.md @@ -1,50 +1,46 @@ # Apache ActiveMQ Artemis Plugin Support Apache ActiveMQ Artemis is designed to allow extra functionality to be added by -creating a plugin. Multiple plugins can be registered at the same time and they will be chained -together and executed in the order they are registered. (i.e. the first plugin registered -is always executed first). +creating a plugin. Multiple plugins can be registered at the same time and they +will be chained together and executed in the order they are registered (i.e. +the first plugin registered is always executed first). -Creating a plugin is very simple. It requires implementing the [`ActiveMQServerPlugin`](https://github.com/apache/activemq-artemis/blob/master/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java) -interface, making sure the plugin is on the classpath, and registering it with the broker. Only the methods that you want to add behavior for need to be implemented as all of the interface methods are default methods. +Creating a plugin is very simple. It requires: -## Adding the plugin to the classpath +- Implementing the [`ActiveMQServerPlugin`](https://github.com/apache/activemq-artemis/blob/master/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/plugin/ActiveMQServerPlugin.java) + interface +- Making sure the plugin is [on the classpath](using-server.md#adding-runtime-dependencies) +- Registering it with the broker either via [xml](#registering-a-plugin) or [programmatically](#registering-a-plugin-programmatically). -See the documentation on [adding runtime dependencies](using-server.md) to understand how to make your plugin available to the broker. - -If you are using an embed system than you will need the jar under the regular classpath of your embedded application. +Only the methods that you want to add behavior for need to be implemented as +all of the interface methods are default methods. ## Registering a Plugin -To register a plugin with by XML you need to add the `broker-plugins` element at the `broker.xml`. It is also possible -to pass configuration to a plugin using the `property` child element(s). These properties (zero to many) -will be read and passed into the Plugin's `init(Map)` operation after the plugin -has been instantiated. +To register a plugin with by XML you need to add the `broker-plugins` element +at the `broker.xml`. It is also possible to pass configuration to a plugin +using the `property` child element(s). These properties (zero to many) will be +read and passed into the plugin's `init(Map)` operation after +the plugin has been instantiated. ```xml - - - ... - - - - - - - ... - - + + + + + + ``` ## Registering a Plugin Programmatically For registering a plugin programmatically you need to call the -registerBrokerPlugin() method and pass in a new instance of your plugin. In the example below -assuming your plugin is called `UserPlugin`, registering it looks like the following: +`registerBrokerPlugin()` method and pass in a new instance of your plugin. In +the example below assuming your plugin is called `UserPlugin`, registering it +looks like the following: ``` java - ... Configuration config = new ConfigurationImpl(); @@ -53,144 +49,80 @@ Configuration config = new ConfigurationImpl(); config.registerBrokerPlugin(new UserPlugin()); ``` -## Using the LoggingActiveMQServerPlugin - -The LoggingActiveMQServerPlugin logs specific broker events. - -You can select which events are logged by setting the following configuration properties to `true`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyProperty Description
LOG_CONNECTION_EVENTSLog info when a Connection is created/destroy. Default `false`.
LOG_SESSION_EVENTSLog info when a Session is created/closed. Default `false`.
LOG_CONSUMER_EVENTSLogs info when a Consumer is created/closed. Default `false`.
LOG_DELIVERING_EVENTSLogs info when message is delivered to a consumer and when a message is acknowledged by a consumer. - Default `false`
LOG_SENDING_EVENTSLogs info when a message has been sent to an address and when a message has been routed within the broker. - Default `false`
LOG_INTERNAL_EVENTSLogs info when a queue created/destroyed, when a message is expired, when a bridge is deployed and when a critical - failure occurs. Default `false`
LOG_ALL_EVENTSLogs info for all the above events. Default `false`
- -By default the LoggingActiveMQServerPlugin wil not log any information. The logging is activated by setting one (or a selection) -of the above configuration properties to `true`. - -To configure the plugin, you can add the following configuration to the broker. In the example below both LOG_DELIVERING_EVENTS -and LOG_SENDING_EVENTS will be logged by the broker. +## Using the `LoggingActiveMQServerPlugin` -```xml - +The `LoggingActiveMQServerPlugin` logs specific broker events. -... - - - - - - -... +You can select which events are logged by setting the following configuration +properties to `true`. + +Property|Trigger Event|Default Value +---|---|--- +`LOG_CONNECTION_EVENTS`|Connection is created/destroy.|`false` +`LOG_SESSION_EVENTS`|Session is created/closed.|`false` +`LOG_CONSUMER_EVENTS`|Consumer is created/closed|`false` +`LOG_DELIVERING_EVENTS`|Message is delivered to a consumer and when a message is acknowledged by a consumer.|`false` +`LOG_SENDING_EVENTS`|When a message has been sent to an address and when a message has been routed within the broker.|`false` +`LOG_INTERNAL_EVENTS`|When a queue created/destroyed, when a message is expired, when a bridge is deployed and when a critical failure occurs.|`false` +`LOG_ALL_EVENTS`|Includes all the above events.|`false` + +By default the `LoggingActiveMQServerPlugin` will not log any information. The +logging is activated by setting one (or a selection) of the above configuration +properties to `true`. + +To configure the plugin, you can add the following configuration to the broker. +In the example below both `LOG_DELIVERING_EVENTS` and `LOG_SENDING_EVENTS` will +be logged by the broker. - +```xml + + + + + + ``` -Most events in the LoggingActiveMQServerPlugin follow a `beforeX` and `afterX` notification pattern e.g beforeCreateConsumer() and afterCreateConsumer(). +Most events in the `LoggingActiveMQServerPlugin` follow a `beforeX` and +`afterX` notification pattern (e.g `beforeCreateConsumer()` and +`afterCreateConsumer()`). -At Log Level `INFO`, the LoggingActiveMQServerPlugin logs an entry when an `afterX` notification occurs. By setting the Logger -"org.apache.activemq.artemis.core.server.plugin.impl" to `DEBUG` Level, log entries are generated for both `beforeX` and `afterX` notifications. -Log Level `DEBUG` will also log more information for a notification when available. +At Log Level `INFO`, the LoggingActiveMQServerPlugin logs an entry when an +`afterX` notification occurs. By setting the logger +`org.apache.activemq.artemis.core.server.plugin.impl` to `DEBUG`, log entries +are generated for both `beforeX` and `afterX` notifications. Log level `DEBUG` +will also log more information for a notification when available. ## Using the NotificationActiveMQServerPlugin -The NotificationActiveMQServerPlugin can be configured to send extra notifications for specific broker events. - -You can select which notifications are sent by setting the following configuration properties to `true`. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyProperty Description
SEND_CONNECTION_NOTIFICATIONSSends a notification when a Connection is created/destroy. Default `false`.
SEND_SESSION_NOTIFICATIONSSends a notification when a Session is created/closed. Default `false`.
SEND_ADDRESS_NOTIFICATIONSSends a notification when an Address is added/removed. Default `false`.
SEND_DELIVERED_NOTIFICATIONSSends a notification when message is delivered to a consumer. Default `false`
SEND_EXPIRED_NOTIFICATIONSSends a notification when message has been expired by the broker. Default `false`
- -By default the NotificationActiveMQServerPlugin will not send any notifications. The plugin is activated by setting one (or a selection) -of the above configuration properties to `true`. - -To configure the plugin, you can add the following configuration to the broker. In the example below both SEND_CONNECTION_NOTIFICATIONS -and SEND_SESSION_NOTIFICATIONS will be sent by the broker. +The NotificationActiveMQServerPlugin can be configured to send extra +notifications for specific broker events. -```xml - +You can select which notifications are sent by setting the following +configuration properties to `true`. -... - - - - - - -... +Property|Property Description|Default Value +---|--- +`SEND_CONNECTION_NOTIFICATIONS`|Sends a notification when a Connection is created/destroy.|`false`. +`SEND_SESSION_NOTIFICATIONS`|Sends a notification when a Session is created/closed.|`false`. +`SEND_ADDRESS_NOTIFICATIONS`|Sends a notification when an Address is added/removed.|`false`. +`SEND_DELIVERED_NOTIFICATIONS`|Sends a notification when message is delivered to a consumer.|`false` +`SEND_EXPIRED_NOTIFICATIONS`|Sends a notification when message has been expired by the broker.|`false` - +By default the NotificationActiveMQServerPlugin will not send any +notifications. The plugin is activated by setting one (or a selection) of the +above configuration properties to `true`. + +To configure the plugin, you can add the following configuration to the broker. +In the example below both `SEND_CONNECTION_NOTIFICATIONS` and +`SEND_SESSION_NOTIFICATIONS` will be sent by the broker. + +```xml + + + + + + ``` diff --git a/docs/user-manual/en/client-classpath.md b/docs/user-manual/en/client-classpath.md index ea61c3a014c..b2820fed5e9 100644 --- a/docs/user-manual/en/client-classpath.md +++ b/docs/user-manual/en/client-classpath.md @@ -4,12 +4,13 @@ Apache ActiveMQ Artemis requires just a single jar on the *client classpath*. > **Warning** > -> The client jar mentioned here can be found in the `lib/client` directory of the -> Apache ActiveMQ Artemis distribution. Be sure you only use the jar from the correct -> version of the release, you *must not* mix and match versions of jars -> from different Apache ActiveMQ Artemis versions. Mixing and matching different jar -> versions may cause subtle errors and failures to occur. +> The client jar mentioned here can be found in the `lib/client` directory of +> the Apache ActiveMQ Artemis distribution. Be sure you only use the jar from +> the correct version of the release, you *must not* mix and match versions of +> jars from different Apache ActiveMQ Artemis versions. Mixing and matching +> different jar versions may cause subtle errors and failures to occur. -Whether you are using JMS or just the Core API simply add the `artemis-jms-client-all.jar` -from the `lib/client` directory to your client classpath. This is a "shaded" jar that -contains all the Artemis code plus dependencies (e.g. JMS spec, Netty, etc.). \ No newline at end of file +Whether you are using JMS or just the Core API simply add the +`artemis-jms-client-all.jar` from the `lib/client` directory to your client +classpath. This is a "shaded" jar that contains all the Artemis code plus +dependencies (e.g. JMS spec, Netty, etc.). diff --git a/docs/user-manual/en/client-reconnection.md b/docs/user-manual/en/client-reconnection.md index dec09096c7c..0c9a99529ae 100644 --- a/docs/user-manual/en/client-reconnection.md +++ b/docs/user-manual/en/client-reconnection.md @@ -6,111 +6,102 @@ connection between the client and the server. ## 100% Transparent session re-attachment -If the failure was due to some transient failure such as a temporary -network failure, and the target server was not restarted, then the -sessions will still be existent on the server, assuming the client -hasn't been disconnected for more than connection-ttl [Detecting Dead Connections](connection-ttl.md) +If the failure was due to some transient failure such as a temporary network +failure, and the target server was not restarted, then the sessions will still +be existent on the server, assuming the client hasn't been disconnected for +more than [connection-ttl](connection-ttl.md) -In this scenario, Apache ActiveMQ Artemis will automatically re-attach the client -sessions to the server sessions when the connection reconnects. This is -done 100% transparently and the client can continue exactly as if -nothing had happened. +In this scenario, Apache ActiveMQ Artemis will automatically re-attach the +client sessions to the server sessions when the connection reconnects. This is +done 100% transparently and the client can continue exactly as if nothing had +happened. The way this works is as follows: -As Apache ActiveMQ Artemis clients send commands to their servers they store each sent -command in an in-memory buffer. In the case that connection failure -occurs and the client subsequently reattaches to the same server, as -part of the reattachment protocol the server informs the client during -reattachment with the id of the last command it successfully received -from that client. +As Apache ActiveMQ Artemis clients send commands to their servers they store +each sent command in an in-memory buffer. In the case that connection failure +occurs and the client subsequently reattaches to the same server, as part of +the reattachment protocol the server informs the client during reattachment +with the id of the last command it successfully received from that client. -If the client has sent more commands than were received before failover -it can replay any sent commands from its buffer so that the client and -server can reconcile their states.Ac +If the client has sent more commands than were received before failover it can +replay any sent commands from its buffer so that the client and server can +reconcile their states.Ac The size of this buffer is configured with the `confirmationWindowSize` parameter on the connection URL. When the server has received -`confirmationWindowSize` bytes of commands and processed them it will -send back a command confirmation to the client, and the client can then -free up space in the buffer. +`confirmationWindowSize` bytes of commands and processed them it will send back +a command confirmation to the client, and the client can then free up space in +the buffer. The window is specified in bytes. Setting this parameter to `-1` disables any buffering and prevents any -re-attachment from occurring, forcing reconnect instead. The default -value for this parameter is `-1`. (Which means by default no auto -re-attachment will occur) +re-attachment from occurring, forcing reconnect instead. The default value for +this parameter is `-1`. (Which means by default no auto re-attachment will +occur) ## Session reconnection -Alternatively, the server might have actually been restarted after -crashing or being stopped. In this case any sessions will no longer be -existent on the server and it won't be possible to 100% transparently -re-attach to them. +Alternatively, the server might have actually been restarted after crashing or +being stopped. In this case any sessions will no longer be existent on the +server and it won't be possible to 100% transparently re-attach to them. -In this case, Apache ActiveMQ Artemis will automatically reconnect the connection and -*recreate* any sessions and consumers on the server corresponding to the -sessions and consumers on the client. This process is exactly the same -as what happens during failover onto a backup server. +In this case, Apache ActiveMQ Artemis will automatically reconnect the +connection and *recreate* any sessions and consumers on the server +corresponding to the sessions and consumers on the client. This process is +exactly the same as what happens during failover onto a backup server. -Client reconnection is also used internally by components such as core -bridges to allow them to reconnect to their target servers. +Client reconnection is also used internally by components such as core bridges +to allow them to reconnect to their target servers. -Please see the section on failover [Automatic Client Failover](ha.md) to get a full understanding of how -transacted and non-transacted sessions are reconnected during -failover/reconnect and what you need to do to maintain *once and only -once*delivery guarantees. +Please see the section on failover [Automatic Client Failover](ha.md) to get a +full understanding of how transacted and non-transacted sessions are +reconnected during failover/reconnect and what you need to do to maintain *once +and only once* delivery guarantees. ## Configuring reconnection/reattachment attributes Client reconnection is configured using the following parameters: -- `retryInterval`. This optional parameter determines the period in - milliseconds between subsequent reconnection attempts, if the - connection to the target server has failed. The default value is - `2000` milliseconds. +- `retryInterval`. This optional parameter determines the period in + milliseconds between subsequent reconnection attempts, if the connection to + the target server has failed. The default value is `2000` milliseconds. -- `retryIntervalMultiplier`. This optional parameter determines - determines a multiplier to apply to the time since the last retry to - compute the time to the next retry. +- `retryIntervalMultiplier`. This optional parameter determines determines a + multiplier to apply to the time since the last retry to compute the time to + the next retry. - This allows you to implement an *exponential backoff* between retry - attempts. + This allows you to implement an *exponential backoff* between retry attempts. - Let's take an example: + Let's take an example: - If we set `retryInterval` to `1000` ms and we set - `retryIntervalMultiplier` to `2.0`, then, if the first reconnect - attempt fails, we will wait `1000` ms then `2000` ms then `4000` ms - between subsequent reconnection attempts. + If we set `retryInterval` to `1000` ms and we set `retryIntervalMultiplier` + to `2.0`, then, if the first reconnect attempt fails, we will wait `1000` ms + then `2000` ms then `4000` ms between subsequent reconnection attempts. - The default value is `1.0` meaning each reconnect attempt is spaced - at equal intervals. + The default value is `1.0` meaning each reconnect attempt is spaced at equal + intervals. -- `maxRetryInterval`. This optional parameter determines the maximum - retry interval that will be used. When setting - `retryIntervalMultiplier` it would otherwise be possible that - subsequent retries exponentially increase to ridiculously large - values. By setting this parameter you can set an upper limit on that - value. The default value is `2000` milliseconds. +- `maxRetryInterval`. This optional parameter determines the maximum retry + interval that will be used. When setting `retryIntervalMultiplier` it would + otherwise be possible that subsequent retries exponentially increase to + ridiculously large values. By setting this parameter you can set an upper limit + on that value. The default value is `2000` milliseconds. -- `reconnectAttempts`. This optional parameter determines the total - number of reconnect attempts to make before giving up and shutting - down. A value of `-1` signifies an unlimited number of attempts. The - default value is `0`. +- `reconnectAttempts`. This optional parameter determines the total number of + reconnect attempts to make before giving up and shutting down. A value of + `-1` signifies an unlimited number of attempts. The default value is `0`. All of these parameters are set on the URL used to connect to the broker. -If your client does manage to reconnect but the session is no longer -available on the server, for instance if the server has been restarted -or it has timed out, then the client won't be able to re-attach, and any -`ExceptionListener` or `FailureListener` instances registered on the -connection or session will be called. +If your client does manage to reconnect but the session is no longer available +on the server, for instance if the server has been restarted or it has timed +out, then the client won't be able to re-attach, and any `ExceptionListener` or +`FailureListener` instances registered on the connection or session will be +called. -ExceptionListeners and SessionFailureListeners -============================================== +## ExceptionListeners and SessionFailureListeners -Please note, that when a client reconnects or re-attaches, any -registered JMS `ExceptionListener` or core API `SessionFailureListener` -will be called. +Please note, that when a client reconnects or re-attaches, any registered JMS +`ExceptionListener` or core API `SessionFailureListener` will be called. diff --git a/docs/user-manual/en/clusters.md b/docs/user-manual/en/clusters.md index b3f4a770480..54dc54e19b6 100644 --- a/docs/user-manual/en/clusters.md +++ b/docs/user-manual/en/clusters.md @@ -46,13 +46,13 @@ connect to them with the minimum of configuration. Server discovery is a mechanism by which servers can propagate their connection details to: -- Messaging clients. A messaging client wants to be able to connect to - the servers of the cluster without having specific knowledge of - which servers in the cluster are up at any one time. +- Messaging clients. A messaging client wants to be able to connect to + the servers of the cluster without having specific knowledge of + which servers in the cluster are up at any one time. -- Other servers. Servers in a cluster want to be able to create - cluster connections to each other without having prior knowledge of - all the other servers in the cluster. +- Other servers. Servers in a cluster want to be able to create + cluster connections to each other without having prior knowledge of + all the other servers in the cluster. This information, let's call it the Cluster Topology, is actually sent around normal Apache ActiveMQ Artemis connections to clients and to other servers over @@ -94,12 +94,12 @@ Let's take a look at an example broadcast group from ```xml - 172.16.9.3 - 5432 - 231.7.7.7 - 9876 - 2000 - netty-connector + 172.16.9.3 + 5432 + 231.7.7.7 + 9876 + 2000 + netty-connector ``` @@ -108,64 +108,66 @@ Some of the broadcast group parameters are optional and you'll normally use the defaults, but we specify them all in the above example for clarity. Let's discuss each one in turn: -- `name` attribute. Each broadcast group in the server must have a - unique name. +- `name` attribute. Each broadcast group in the server must have a + unique name. -- `local-bind-address`. This is the local bind address that the - datagram socket is bound to. If you have multiple network interfaces - on your server, you would specify which one you wish to use for - broadcasts by setting this property. If this property is not - specified then the socket will be bound to the wildcard address, an - IP address chosen by the kernel. This is a UDP specific attribute. +- `local-bind-address`. This is the local bind address that the + datagram socket is bound to. If you have multiple network interfaces + on your server, you would specify which one you wish to use for + broadcasts by setting this property. If this property is not + specified then the socket will be bound to the wildcard address, an + IP address chosen by the kernel. This is a UDP specific attribute. -- `local-bind-port`. If you want to specify a local port to which the - datagram socket is bound you can specify it here. Normally you would - just use the default value of `-1` which signifies that an anonymous - port should be used. This parameter is always specified in - conjunction with `local-bind-address`. This is a UDP specific - attribute. +- `local-bind-port`. If you want to specify a local port to which the + datagram socket is bound you can specify it here. Normally you would + just use the default value of `-1` which signifies that an anonymous + port should be used. This parameter is always specified in + conjunction with `local-bind-address`. This is a UDP specific + attribute. -- `group-address`. This is the multicast address to which the data - will be broadcast. It is a class D IP address in the range - `224.0.0.0` to `239.255.255.255`, inclusive. The address `224.0.0.0` - is reserved and is not available for use. This parameter is - mandatory. This is a UDP specific attribute. +- `group-address`. This is the multicast address to which the data + will be broadcast. It is a class D IP address in the range + `224.0.0.0` to `239.255.255.255`, inclusive. The address `224.0.0.0` + is reserved and is not available for use. This parameter is + mandatory. This is a UDP specific attribute. -- `group-port`. This is the UDP port number used for broadcasting. - This parameter is mandatory. This is a UDP specific attribute. +- `group-port`. This is the UDP port number used for broadcasting. + This parameter is mandatory. This is a UDP specific attribute. -- `broadcast-period`. This is the period in milliseconds between - consecutive broadcasts. This parameter is optional, the default - value is `2000` milliseconds. +- `broadcast-period`. This is the period in milliseconds between + consecutive broadcasts. This parameter is optional, the default + value is `2000` milliseconds. -- `connector-ref`. This specifies the connector and optional backup - connector that will be broadcasted (see [Configuring the Transport](configuring-transports.md) for more information on - connectors). +- `connector-ref`. This specifies the connector and optional backup + connector that will be broadcasted (see [Configuring the Transport](configuring-transports.md) for more information on + connectors). Here is another example broadcast group that defines a JGroups broadcast group: - - - test-jgroups-file_ping.xml - activemq_broadcast_channel - 2000 - netty-connector - - +```xml + + + test-jgroups-file_ping.xml + activemq_broadcast_channel + 2000 + netty-connector + + +``` To be able to use JGroups to broadcast, one must specify two attributes, i.e. `jgroups-file` and `jgroups-channel`, as discussed in details as following: -- `jgroups-file` attribute. This is the name of JGroups configuration - file. It will be used to initialize JGroups channels. Make sure the - file is in the java resource path so that Apache ActiveMQ Artemis can load it. +- `jgroups-file` attribute. This is the name of JGroups configuration + file. It will be used to initialize JGroups channels. Make sure the + file is in the java resource path so that Apache ActiveMQ Artemis can load it. -- `jgroups-channel` attribute. The name that JGroups channels connect - to for broadcasting. +- `jgroups-channel` attribute. The name that JGroups channels connect + to for broadcasting. -> **Note** +> **Note:** > > The JGroups attributes (`jgroups-file` and `jgroups-channel`) and UDP > specific attributes described above are exclusive of each other. Only @@ -174,57 +176,59 @@ following: The following is an example of a JGroups file - - - - - - - - - - - - - - - - - - +```xml + + + + + + + + + + + + + + + + + + +``` As it shows, the file content defines a jgroups protocol stacks. If you want Apache ActiveMQ Artemis to use this stacks for channel creation, you have to make @@ -233,7 +237,9 @@ configuration to be the name of this jgroups configuration file. For example if the above stacks configuration is stored in a file named "jgroups-stacks.xml" then your `jgroups-file` should be like - jgroups-stacks.xml +```xml +jgroups-stacks.xml +``` #### Discovery Groups @@ -252,18 +258,18 @@ of time it will remove that server's entry from its list. Discovery groups are used in two places in Apache ActiveMQ Artemis: -- By cluster connections so they know how to obtain an initial - connection to download the topology +- By cluster connections so they know how to obtain an initial + connection to download the topology -- By messaging clients so they know how to obtain an initial - connection to download the topology +- By messaging clients so they know how to obtain an initial + connection to download the topology Although a discovery group will always accept broadcasts, its current list of available live and backup servers is only ever used when an initial connection is made, from then server discovery is done over the normal Apache ActiveMQ Artemis connections. -> **Note** +> **Note:** > > Each discovery group must be configured with broadcast endpoint (UDP > or JGroups) that matches its broadcast group counterpart. For example, @@ -277,67 +283,71 @@ configuration file `broker.xml`. All discovery groups must be defined inside a `discovery-groups` element. There can be many discovery groups defined by Apache ActiveMQ Artemis server. Let's look at an example: - - - 172.16.9.7 - 231.7.7.7 - 9876 - 10000 - - +```xml + + + 172.16.9.7 + 231.7.7.7 + 9876 + 10000 + + +``` We'll consider each parameter of the discovery group: -- `name` attribute. Each discovery group must have a unique name per - server. - -- `local-bind-address`. If you are running with multiple network - interfaces on the same machine, you may want to specify that the - discovery group listens only only a specific interface. To do this - you can specify the interface address with this parameter. This - parameter is optional. This is a UDP specific attribute. - -- `group-address`. This is the multicast IP address of the group to - listen on. It should match the `group-address` in the broadcast - group that you wish to listen from. This parameter is mandatory. - This is a UDP specific attribute. - -- `group-port`. This is the UDP port of the multicast group. It should - match the `group-port` in the broadcast group that you wish to - listen from. This parameter is mandatory. This is a UDP specific - attribute. - -- `refresh-timeout`. This is the period the discovery group waits - after receiving the last broadcast from a particular server before - removing that servers connector pair entry from its list. You would - normally set this to a value significantly higher than the - `broadcast-period` on the broadcast group otherwise servers might - intermittently disappear from the list even though they are still - broadcasting due to slight differences in timing. This parameter is - optional, the default value is `10000` milliseconds (10 seconds). +- `name` attribute. Each discovery group must have a unique name per + server. + +- `local-bind-address`. If you are running with multiple network + interfaces on the same machine, you may want to specify that the + discovery group listens only only a specific interface. To do this + you can specify the interface address with this parameter. This + parameter is optional. This is a UDP specific attribute. + +- `group-address`. This is the multicast IP address of the group to + listen on. It should match the `group-address` in the broadcast + group that you wish to listen from. This parameter is mandatory. + This is a UDP specific attribute. + +- `group-port`. This is the UDP port of the multicast group. It should + match the `group-port` in the broadcast group that you wish to + listen from. This parameter is mandatory. This is a UDP specific + attribute. + +- `refresh-timeout`. This is the period the discovery group waits + after receiving the last broadcast from a particular server before + removing that servers connector pair entry from its list. You would + normally set this to a value significantly higher than the + `broadcast-period` on the broadcast group otherwise servers might + intermittently disappear from the list even though they are still + broadcasting due to slight differences in timing. This parameter is + optional, the default value is `10000` milliseconds (10 seconds). Here is another example that defines a JGroups discovery group: - - - test-jgroups-file_ping.xml - activemq_broadcast_channel - 10000 - - +```xml + + + test-jgroups-file_ping.xml + activemq_broadcast_channel + 10000 + + +``` To receive broadcast from JGroups channels, one must specify two attributes, `jgroups-file` and `jgroups-channel`, as discussed in details as following: -- `jgroups-file` attribute. This is the name of JGroups configuration - file. It will be used to initialize JGroups channels. Make sure the - file is in the java resource path so that Apache ActiveMQ Artemis can load it. +- `jgroups-file` attribute. This is the name of JGroups configuration + file. It will be used to initialize JGroups channels. Make sure the + file is in the java resource path so that Apache ActiveMQ Artemis can load it. -- `jgroups-channel` attribute. The name that JGroups channels connect - to for receiving broadcasts. +- `jgroups-channel` attribute. The name that JGroups channels connect + to for receiving broadcasts. -> **Note** +> **Note:** > > The JGroups attributes (`jgroups-file` and `jgroups-channel`) and UDP > specific attributes described above are exclusive of each other. Only @@ -355,7 +365,9 @@ differs depending on whether you're using JMS or the core API. Use the `udp` URL scheme and a host:port combination matches the group-address and group-port from the corresponding `broadcast-group` on the server: - udp://231.7.7.7:9876 +``` +udp://231.7.7.7:9876 +``` The element `discovery-group-ref` specifies the name of a discovery group defined in `broker.xml`. @@ -402,7 +414,9 @@ A static list of possible servers can also be used by a normal client. A list of servers to be used for the initial connection attempt can be specified in the connection URI using a syntax with `()`, e.g.: - (tcp://myhost:61616,tcp://myhost2:61616)?reconnectAttempts=5 +``` +(tcp://myhost:61616,tcp://myhost2:61616)?reconnectAttempts=5 +``` The brackets are expanded so the same query can be appended after the last bracket for ease. @@ -453,219 +467,223 @@ typical cluster connection. Cluster connections are always defined in There can be zero or more cluster connections defined per Apache ActiveMQ Artemis server. - - -
- netty-connector - 1000 - 5000 - 50000 - 5000 - 500 - 1.0 - 5000 - -1 - -1 - true - ON_DEMAND - 1 - 32000 - 30000 - 1000 - 2 - -
-
+```xml + + +
+ netty-connector + 1000 + 5000 + 50000 + 5000 + 500 + 1.0 + 5000 + -1 + -1 + true + ON_DEMAND + 1 + 32000 + 30000 + 1000 + 2 + +
+
+``` In the above cluster connection all parameters have been explicitly specified. The following shows all the available configuration options -- `address` Each cluster connection only applies to addresses that - match the specified `address` field. An address is matched on the - cluster connection when it begins with the string specified in this - field. The `address` field on a cluster connection also supports comma - separated lists and an exclude syntax `!`. To prevent an address - from being matched on this cluster connection, prepend a cluster - connection address string with `!`. - - In the case shown above the cluster connection will load balance - messages sent to all addresses (since it's empty). - - The address can be any value and you can have many cluster - connections with different values of `address`, simultaneously - balancing messages for those addresses, potentially to different - clusters of servers. By having multiple cluster connections on - different addresses a single Apache ActiveMQ Artemis Server can effectively take - part in multiple clusters simultaneously. - - Be careful not to have multiple cluster connections with overlapping - values of `address`, e.g. "europe" and "europe.news" since this - could result in the same messages being distributed between more - than one cluster connection, possibly resulting in duplicate - deliveries. - - Examples: - - - 'eu' - matches all addresses starting with 'eu' - - '!eu' - matches all address except for those starting with 'eu' - - 'eu.uk,eu.de' - matches all addresses starting with either 'eu.uk' or - 'eu.de' - - 'eu,!eu.uk' - matches all addresses starting with 'eu' but not those - starting with 'eu.uk' - - Notes: - - - Address exclusion will always takes precedence over address - inclusion. - - Address matching on cluster connections does not support - wild-card matching. - -- `connector-ref`. This is the connector which will be sent to other - nodes in the cluster so they have the correct cluster topology. - - This parameter is mandatory. - -- `check-period`. The period (in milliseconds) used to check if the - cluster connection has failed to receive pings from another server. - Default is 30000. - -- `connection-ttl`. This is how long a cluster connection should stay - alive if it stops receiving messages from a specific node in the - cluster. Default is 60000. - -- `min-large-message-size`. If the message size (in bytes) is larger - than this value then it will be split into multiple segments when - sent over the network to other cluster members. Default is 102400. - -- `call-timeout`. When a packet is sent via a cluster connection and - is a blocking call, i.e. for acknowledgements, this is how long it - will wait (in milliseconds) for the reply before throwing an - exception. Default is 30000. - -- `retry-interval`. We mentioned before that, internally, cluster - connections cause bridges to be created between the nodes of the - cluster. If the cluster connection is created and the target node - has not been started, or say, is being rebooted, then the cluster - connections from other nodes will retry connecting to the target - until it comes back up, in the same way as a bridge does. - - This parameter determines the interval in milliseconds between retry - attempts. It has the same meaning as the `retry-interval` on a - bridge (as described in [Core Bridges](core-bridges.md)). - - This parameter is optional and its default value is `500` - milliseconds. - -- `retry-interval-multiplier`. This is a multiplier used to increase - the `retry-interval` after each reconnect attempt, default is 1. - -- `max-retry-interval`. The maximum delay (in milliseconds) for - retries. Default is 2000. - -- `initial-connect-attempts`. The number of times the system will try - to connect a node in the cluster initially. If the max-retry is - achieved this node will be considered permanently down and the - system will not route messages to this node. Default is -1 (infinite - retries). - -- `reconnect-attempts`. The number of times the system will try to - reconnect to a node in the cluster. If the max-retry is achieved - this node will be considered permanently down and the system will - stop routing messages to this node. Default is -1 (infinite - retries). - -- `use-duplicate-detection`. Internally cluster connections use - bridges to link the nodes, and bridges can be configured to add a - duplicate id property in each message that is forwarded. If the - target node of the bridge crashes and then recovers, messages might - be resent from the source node. By enabling duplicate detection any - duplicate messages will be filtered out and ignored on receipt at - the target node. - - This parameter has the same meaning as `use-duplicate-detection` on - a bridge. For more information on duplicate detection, please see [Duplicate Detection](duplicate-detection.md). - Default is true. - -- `message-load-balancing`. This parameter determines if/how - messages will be distributed between other nodes of the cluster. - It can be one of three values - `OFF`, `STRICT`, or `ON_DEMAND` - (default). This parameter replaces the deprecated - `forward-when-no-consumers` parameter. - - If this is set to `OFF` then messages will never be forwarded to - another node in the cluster - - If this is set to `STRICT` then each incoming message will be round - robin'd even though the same queues on the other nodes of the - cluster may have no consumers at all, or they may have consumers - that have non matching message filters (selectors). Note that - Apache ActiveMQ Artemis will *not* forward messages to other nodes - if there are no *queues* of the same name on the other nodes, even - if this parameter is set to `STRICT`. Using `STRICT` is like setting - the legacy `forward-when-no-consumers` parameter to `true`. - - If this is set to `ON_DEMAND` then Apache ActiveMQ Artemis will only - forward messages to other nodes of the cluster if the address to which - they are being forwarded has queues which have consumers, and if those - consumers have message filters (selectors) at least one of those - selectors must match the message. Using `ON_DEMAND` is like setting - the legacy `forward-when-no-consumers` parameter to `false`. - - Default is `ON_DEMAND`. - -- `max-hops`. When a cluster connection decides the set of nodes to - which it might load balance a message, those nodes do not have to be - directly connected to it via a cluster connection. Apache ActiveMQ Artemis can be - configured to also load balance messages to nodes which might be - connected to it only indirectly with other Apache ActiveMQ Artemis servers as - intermediates in a chain. - - This allows Apache ActiveMQ Artemis to be configured in more complex topologies and - still provide message load balancing. We'll discuss this more later - in this chapter. - - The default value for this parameter is `1`, which means messages - are only load balanced to other Apache ActiveMQ Artemis serves which are directly - connected to this server. This parameter is optional. - -- `confirmation-window-size`. The size (in bytes) of the window used - for sending confirmations from the server connected to. So once the - server has received `confirmation-window-size` bytes it notifies its - client, default is 1048576. A value of -1 means no window. - -- `producer-window-size`. The size for producer flow control over cluster connection. - it's by default disabled through the cluster connection bridge but you may want - to set a value if you are using really large messages in cluster. A value of -1 means no window. - -- `call-failover-timeout`. Similar to `call-timeout` but used when a - call is made during a failover attempt. Default is -1 (no timeout). - -- `notification-interval`. How often (in milliseconds) the cluster - connection should broadcast itself when attaching to the cluster. - Default is 1000. - -- `notification-attempts`. How many times the cluster connection - should broadcast itself when connecting to the cluster. Default is - 2. - -- `discovery-group-ref`. This parameter determines which discovery - group is used to obtain the list of other servers in the cluster - that this cluster connection will make connections to. +- `address` Each cluster connection only applies to addresses that + match the specified `address` field. An address is matched on the + cluster connection when it begins with the string specified in this + field. The `address` field on a cluster connection also supports comma + separated lists and an exclude syntax `!`. To prevent an address + from being matched on this cluster connection, prepend a cluster + connection address string with `!`. + + In the case shown above the cluster connection will load balance + messages sent to all addresses (since it's empty). + + The address can be any value and you can have many cluster + connections with different values of `address`, simultaneously + balancing messages for those addresses, potentially to different + clusters of servers. By having multiple cluster connections on + different addresses a single Apache ActiveMQ Artemis Server can effectively take + part in multiple clusters simultaneously. + + Be careful not to have multiple cluster connections with overlapping + values of `address`, e.g. "europe" and "europe.news" since this + could result in the same messages being distributed between more + than one cluster connection, possibly resulting in duplicate + deliveries. + + Examples: + + - 'eu' + matches all addresses starting with 'eu' + - '!eu' + matches all address except for those starting with 'eu' + - 'eu.uk,eu.de' + matches all addresses starting with either 'eu.uk' or + 'eu.de' + - 'eu,!eu.uk' + matches all addresses starting with 'eu' but not those + starting with 'eu.uk' + + **Note:**: + + - Address exclusion will always takes precedence over address + inclusion. + - Address matching on cluster connections does not support + wild-card matching. + +- `connector-ref`. This is the connector which will be sent to other + nodes in the cluster so they have the correct cluster topology. + + This parameter is mandatory. + +- `check-period`. The period (in milliseconds) used to check if the + cluster connection has failed to receive pings from another server. + Default is 30000. + +- `connection-ttl`. This is how long a cluster connection should stay + alive if it stops receiving messages from a specific node in the + cluster. Default is 60000. + +- `min-large-message-size`. If the message size (in bytes) is larger + than this value then it will be split into multiple segments when + sent over the network to other cluster members. Default is 102400. + +- `call-timeout`. When a packet is sent via a cluster connection and + is a blocking call, i.e. for acknowledgements, this is how long it + will wait (in milliseconds) for the reply before throwing an + exception. Default is 30000. + +- `retry-interval`. We mentioned before that, internally, cluster + connections cause bridges to be created between the nodes of the + cluster. If the cluster connection is created and the target node + has not been started, or say, is being rebooted, then the cluster + connections from other nodes will retry connecting to the target + until it comes back up, in the same way as a bridge does. + + This parameter determines the interval in milliseconds between retry + attempts. It has the same meaning as the `retry-interval` on a + bridge (as described in [Core Bridges](core-bridges.md)). + + This parameter is optional and its default value is `500` + milliseconds. + +- `retry-interval-multiplier`. This is a multiplier used to increase + the `retry-interval` after each reconnect attempt, default is 1. + +- `max-retry-interval`. The maximum delay (in milliseconds) for + retries. Default is 2000. + +- `initial-connect-attempts`. The number of times the system will try + to connect a node in the cluster initially. If the max-retry is + achieved this node will be considered permanently down and the + system will not route messages to this node. Default is -1 (infinite + retries). + +- `reconnect-attempts`. The number of times the system will try to + reconnect to a node in the cluster. If the max-retry is achieved + this node will be considered permanently down and the system will + stop routing messages to this node. Default is -1 (infinite + retries). + +- `use-duplicate-detection`. Internally cluster connections use + bridges to link the nodes, and bridges can be configured to add a + duplicate id property in each message that is forwarded. If the + target node of the bridge crashes and then recovers, messages might + be resent from the source node. By enabling duplicate detection any + duplicate messages will be filtered out and ignored on receipt at + the target node. + + This parameter has the same meaning as `use-duplicate-detection` on + a bridge. For more information on duplicate detection, please see [Duplicate Detection](duplicate-detection.md). + Default is true. + +- `message-load-balancing`. This parameter determines if/how + messages will be distributed between other nodes of the cluster. + It can be one of three values - `OFF`, `STRICT`, or `ON_DEMAND` + (default). This parameter replaces the deprecated + `forward-when-no-consumers` parameter. + + If this is set to `OFF` then messages will never be forwarded to + another node in the cluster + + If this is set to `STRICT` then each incoming message will be round + robin'd even though the same queues on the other nodes of the + cluster may have no consumers at all, or they may have consumers + that have non matching message filters (selectors). Note that + Apache ActiveMQ Artemis will *not* forward messages to other nodes + if there are no *queues* of the same name on the other nodes, even + if this parameter is set to `STRICT`. Using `STRICT` is like setting + the legacy `forward-when-no-consumers` parameter to `true`. + + If this is set to `ON_DEMAND` then Apache ActiveMQ Artemis will only + forward messages to other nodes of the cluster if the address to which + they are being forwarded has queues which have consumers, and if those + consumers have message filters (selectors) at least one of those + selectors must match the message. Using `ON_DEMAND` is like setting + the legacy `forward-when-no-consumers` parameter to `false`. + + Default is `ON_DEMAND`. + +- `max-hops`. When a cluster connection decides the set of nodes to + which it might load balance a message, those nodes do not have to be + directly connected to it via a cluster connection. Apache ActiveMQ Artemis can be + configured to also load balance messages to nodes which might be + connected to it only indirectly with other Apache ActiveMQ Artemis servers as + intermediates in a chain. + + This allows Apache ActiveMQ Artemis to be configured in more complex topologies and + still provide message load balancing. We'll discuss this more later + in this chapter. + + The default value for this parameter is `1`, which means messages + are only load balanced to other Apache ActiveMQ Artemis serves which are directly + connected to this server. This parameter is optional. + +- `confirmation-window-size`. The size (in bytes) of the window used + for sending confirmations from the server connected to. So once the + server has received `confirmation-window-size` bytes it notifies its + client, default is 1048576. A value of -1 means no window. + +- `producer-window-size`. The size for producer flow control over cluster connection. + it's by default disabled through the cluster connection bridge but you may want + to set a value if you are using really large messages in cluster. A value of -1 means no window. + +- `call-failover-timeout`. Similar to `call-timeout` but used when a + call is made during a failover attempt. Default is -1 (no timeout). + +- `notification-interval`. How often (in milliseconds) the cluster + connection should broadcast itself when attaching to the cluster. + Default is 1000. + +- `notification-attempts`. How many times the cluster connection + should broadcast itself when connecting to the cluster. Default is + 2. + +- `discovery-group-ref`. This parameter determines which discovery + group is used to obtain the list of other servers in the cluster + that this cluster connection will make connections to. Alternatively if you would like your cluster connections to use a static list of servers for discovery then you can do it like this. - - ... - - server0-connector - server1-connector - - +```xml + + ... + + server0-connector + server1-connector + + +``` Here we have defined 2 servers that we know for sure will that at least one will be available. There may be many more servers in the cluster but @@ -678,8 +696,10 @@ When creating connections between nodes of a cluster to form a cluster connection, Apache ActiveMQ Artemis uses a cluster user and cluster password which is defined in `broker.xml`: - ACTIVEMQ.CLUSTER.ADMIN.USER - CHANGE ME!! +```xml +ACTIVEMQ.CLUSTER.ADMIN.USER +CHANGE ME!! +``` > **Warning** > @@ -701,35 +721,35 @@ policies, and you can also implement your own and use that. The out-of-the-box policies are -- Round Robin. With this policy the first node is chosen randomly then - each subsequent node is chosen sequentially in the same order. +- Round Robin. With this policy the first node is chosen randomly then + each subsequent node is chosen sequentially in the same order. - For example nodes might be chosen in the order B, C, D, A, B, C, D, - A, B or D, A, B, C, D, A, B, C, D or C, D, A, B, C, D, A, B, C. + For example nodes might be chosen in the order B, C, D, A, B, C, D, + A, B or D, A, B, C, D, A, B, C, D or C, D, A, B, C, D, A, B, C. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy` + as the ``. -- Random. With this policy each node is chosen randomly. +- Random. With this policy each node is chosen randomly. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy` + as the ``. -- Random Sticky. With this policy the first node is chosen randomly - and then re-used for subsequent connections. +- Random Sticky. With this policy the first node is chosen randomly + and then re-used for subsequent connections. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.RandomStickyConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.RandomStickyConnectionLoadBalancingPolicy` + as the ``. -- First Element. With this policy the "first" (i.e. 0th) node is - always returned. +- First Element. With this policy the "first" (i.e. 0th) node is + always returned. - Use - `org.apache.activemq.artemis.api.core.client.loadbalance.FirstElementConnectionLoadBalancingPolicy` - as the ``. + Use + `org.apache.activemq.artemis.api.core.client.loadbalance.FirstElementConnectionLoadBalancingPolicy` + as the ``. You can also implement your own policy by implementing the interface `org.apache.activemq.artemis.api.core.client.loadbalance.ConnectionLoadBalancingPolicy` @@ -742,15 +762,17 @@ default will be used which is The parameter `connectionLoadBalancingPolicyClassName` can be set on the URI to configure what load balancing policy to use: - tcp://localhost:61616?connectionLoadBalancingPolicyClassName=org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy +``` +tcp://localhost:61616?connectionLoadBalancingPolicyClassName=org.apache.activemq.artemis.api.core.client.loadbalance.RandomConnectionLoadBalancingPolicy +``` The set of servers over which the factory load balances can be determined in one of two ways: -- Specifying servers explicitly in the URL. This also requires setting - the `useTopologyForLoadBalancing` parameter to `false` on the URL. +- Specifying servers explicitly in the URL. This also requires setting + the `useTopologyForLoadBalancing` parameter to `false` on the URL. -- Using discovery. This is the default behavior. +- Using discovery. This is the default behavior. ## Specifying Members of a Cluster Explicitly @@ -760,17 +782,19 @@ typically used to form non symmetrical clusters such as chain cluster or ring clusters. This can only be done using a static list of connectors and is configured as follows: - -
jms
- netty-connector - 500 - true - STRICT - 1 - - server1-connector - -
+```xml + +
+ netty-connector + 500 + true + STRICT + 1 + + server1-connector + + +``` In this example we have set the attribute `allow-direct-connections-only` which means that the only server that @@ -808,11 +832,13 @@ information on configuring address settings, please see [Configuring Addresses a Here's an address settings snippet from `broker.xml` showing how message redistribution is enabled for a set of queues: - - - 0 - - +```xml + + + 0 + + +``` The above `address-settings` block would set a `redistribution-delay` of `0` for any queue which is bound to any address. So the above would enable diff --git a/docs/user-manual/en/config-reload.md b/docs/user-manual/en/config-reload.md index 1ba76850b3b..630a13d6f07 100644 --- a/docs/user-manual/en/config-reload.md +++ b/docs/user-manual/en/config-reload.md @@ -1,8 +1,11 @@ # Configuration Reload -The system will perform a periodic check on the configuration files, configured by `configuration-file-refresh-period`, with the default at 5000, in milliseconds. +The system will perform a periodic check on the configuration files, configured +by `configuration-file-refresh-period`, with the default at 5000, in +milliseconds. -Once the configuration file is changed (broker.xml) the following modules will be reloaded automatically: +Once the configuration file is changed (broker.xml) the following modules will +be reloaded automatically: - Address Settings - Security Settings @@ -10,7 +13,7 @@ Once the configuration file is changed (broker.xml) the following modules will b - Addresses & queues -Notice: +**Note:** Deletion of Address's and Queue's, not auto created is controlled by Address Settings @@ -23,50 +26,63 @@ Deletion of Address's and Queue's, not auto created is controlled by Address Set * FORCE - will remove the queue upon config reload, even if messages remains, losing the messages in the queue. -By default both settings are OFF as such address & queues won't be removed upon reload, given the risk of losing messages. +By default both settings are OFF as such address & queues won't be removed upon +reload, given the risk of losing messages. -When OFF You may execute explicit CLI or Management operations to remove address & queues. +When OFF You may execute explicit CLI or Management operations to remove +address & queues. ## Reloadable Parameters -The broker configuration file has 2 main parts, `` and ``. Some of the parameters in the 2 parts are monitored and, -if modified, reloaded into the broker at runtime. +The broker configuration file has 2 main parts, `` and ``. Some of +the parameters in the 2 parts are monitored and, if modified, reloaded into the +broker at runtime. -Please note that elements under `` are deprecated. Users are encouraged to use `` configuration entities. +**Note:** Elements under `` are **deprecated**. Users are encouraged to +use `` configuration entities. -> *Note:* -> Most parameters reloaded take effect immediately after reloading. However there are some -> that won’t take any effect unless you restarting the broker. +> **Note:** +> +> Most parameters reloaded take effect immediately after reloading. However +> there are some that won’t take any effect unless you restarting the broker. > Such parameters are specifically indicated in the following text. -### `` +### `` #### `` * `` element -Changes to any elements will be reloaded. Each defines security roles for a matched address. +Changes to any `` elements will be reloaded. Each +`` defines security roles for a matched address. - * The `match` attribute +* The `match` attribute - This attribute defines the address for which the security-setting is defined. It can take wildcards such as ‘#’ and ‘*’. + This attribute defines the address for which the security-setting is + defined. It can take wildcards such as ‘#’ and ‘*’. - * The `` sub-elements +* The `` sub-elements - Each `` can have a list of `` elements, each of which defines a specific permission-roles mapping. - Each permission has 2 attributes ‘type’ and ‘roles’. The ‘type’ attribute defines the type of operation allowed, the ‘roles’ - defines which roles are allowed to perform such operation. Refer to the user’s manual for a list of operations that can be defined. +Each `` can have a list of `` elements, each +of which defines a specific permission-roles mapping. Each permission has 2 +attributes ‘type’ and ‘roles’. The ‘type’ attribute defines the type of +operation allowed, the ‘roles’ defines which roles are allowed to perform such +operation. Refer to the user’s manual for a list of operations that can be +defined. -> *Note:* -> Once loaded the security-settings will take effect immediately. Any new clients will subject -> to the new security settings. Any existing clients will subject to the new settings as well, as soon as they performs -> a new security-sensitive operation. +> **Note:** +> +> Once loaded the security-settings will take effect immediately. Any new +> clients will subject to the new security settings. Any existing clients will +> subject to the new settings as well, as soon as they performs a new +> security-sensitive operation. -Below lists the effects of adding, deleting and updating of an element/attribute within the `` element, whether -an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, whether an change +can be done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X* (at most one element is allowed) | Deleting it means delete the whole security settings from the running broker. | N/A* `` | Adding one element means adding a new set of security roles for an address in the running broker | Deleting one element means removing a set of security roles for an address in the running broker | Updating one element means updating the security roles for an address (if match attribute is not changed), or means removing the old match address settings and adding a new one (if match attribute is changed) attribute `match` | N/A* | X* | Changing this value is same as deleting the whole with the old match value and adding @@ -81,21 +97,28 @@ attribute `roles` | N/A* | X* | Changing the ‘roles’ value means updating th * `` element -Changes to elements under `` will be reloaded into runtime broker. It contains a list of `` elements. +Changes to elements under `` will be reloaded into runtime +broker. It contains a list of `` elements. * `` element - - Each address-setting element has a ‘match’ attribute that defines an address pattern for which this address-setting is defined. It also has a list of sub-elements used to define the properties of a matching address. - - > *Note:* - > Parameters reloaded in this category will take effect immediately after reloading. The effect of deletion of Address's and Queue's, - > not auto created is controlled by parameter `config-delete-addresses` and `config-delete-queues` as described in the doc. - -Below lists the effects of adding, deleting and updating of an element/attribute within the address-settings element, whether an change -can be done or can’t be done. + + Each address-setting element has a ‘match’ attribute that defines an address + pattern for which this address-setting is defined. It also has a list of + sub-elements used to define the properties of a matching address. + + > **Note:** + > + > Parameters reloaded in this category will take effect immediately + > after reloading. The effect of deletion of Address's and Queue's, not auto + > created is controlled by parameter `config-delete-addresses` and + > `config-delete-queues` as described in the doc. + +Below lists the effects of adding, deleting and updating of an +element/attribute within the address-settings element, whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(at most one element is allowed) | Deleting it means delete the whole address settings from the running broker | N/A `` | Adding one element means adding a set of address-setting for a new address in the running broker | Deleting one means removing a set of address-setting for an address in the running broker | Updating one element means updating the address setting for an address (if match attribute is not changed), or means removing the old match address settings and adding a new one (if match attribute is changed) attribute `match` | N/A | X | Changing this value is same as deleting the whole with the old match value and adding a new one with the new match value. @@ -132,20 +155,22 @@ attribute `match` | N/A | X | Changing this value is same as deleting the whole #### `` -All `` elements will be reloaded. Each `` element -has a ‘name’ and several sub-elements that defines the properties of a divert. +All `` elements will be reloaded. Each `` element has a ‘name’ +and several sub-elements that defines the properties of a divert. -> *Note:* -> Reloading `` only resulting in deploying new diverts. Existing diverts -> won’t get undeployed even if you delete a `` element. Nor an existing -> divert will be updated if its element is updated after reloading. -> To make this happen you need a restart of the broker. +> **Note:** +> +> Reloading `` only resulting in deploying new diverts. Existing diverts +> won’t get undeployed even if you delete a `` element. Nor an existing +> divert will be updated if its element is updated after reloading. To make +> this happen you need a restart of the broker. -Below lists the effects of adding, deleting and updating of an element/attribute -within the diverts element, whether an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the diverts element, whether an change can be done or +can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X (no more than one can be present) | Deleting it means delete (undeploy) all diverts in running broker. | N/A `` | Adding a new divert. It will be deployed after reloading | No effect on the deployed divert.(unless restarting broker, in which case the divert will no longer be deployed) | No effect on the deployed divert (unless restarting broker, in which case the divert will be redeployed) attribute `name` | N/A | X | A new divert with the name will be deployed. (if it is not already there in broker). Otherwise no effect. @@ -159,23 +184,28 @@ attribute `name` | N/A | X | A new divert with the name will be deployed. (if it #### `` -The `` element contains a list `
` elements. Once changed, all `
` elements - in `` will be reloaded. +The `` element contains a list `
` elements. Once changed, +all `
` elements in `` will be reloaded. -> *Note:* -> Once reloaded, all new addresses (as well as the pre-configured queues) will be -> deployed to the running broker and all those that are missing from the configuration will be undeployed. +> **Note:** +> +> Once reloaded, all new addresses (as well as the pre-configured queues) will +> be deployed to the running broker and all those that are missing from the +> configuration will be undeployed. -> *Note:* -> Parameters reloaded in this category will take effect immediately after reloading. -> The effect of deletion of Address's and Queue's, not auto created is controlled by -> parameter `config-delete-addresses` and `config-delete-queues` as described in this doc. +> **Note:** +> +> Parameters reloaded in this category will take effect immediately after +> reloading. The effect of deletion of Address's and Queue's, not auto created +> is controlled by parameter `config-delete-addresses` and +> `config-delete-queues` as described in this doc. -Below lists the effects of adding, deleting and updating of an element/attribute -within the `` element, whether an change can be done or can’t be done. +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(no more than one is present) | Deleting it means delete (undeploy) all diverts in running broker. | N/A `
` | A new address will be deployed in the running broker | The corresponding address will be undeployed. | N/A attribute `name` | N/A | X | After reloading the address of the old name will be undeployed and the new will be deployed. @@ -186,21 +216,27 @@ attribute `name` | N/A | X | After reloading the address of the old name will be #### `` -The `` element contains a list `` elements. Once changed, all `` elements in `` will be reloaded. +The `` element contains a list `` elements. Once changed, all +`` elements in `` will be reloaded. -> *Note:* -> Once reloaded, all new queues will be deployed to the running broker and all +> **Note:** +> +> Once reloaded, all new queues will be deployed to the running broker and all > queues that are missing from the configuration will be undeployed. -> *Note:* -> Parameters reloaded in this category will take effect immediately after reloading. -> The effect of deletion of Address's and Queue's, not auto created is controlled by -> parameter `config-delete-addresses` and `config-delete-queues` as described in this doc. -Below lists the effects of adding, deleting and updating of an element/attribute within the `` element, -and whether an change can be done or can’t be done. +> **Note:** +> +> Parameters reloaded in this category will take effect immediately after +> reloading. The effect of deletion of Address's and Queue's, not auto created +> is controlled by parameter `config-delete-addresses` and +> `config-delete-queues` as described in this doc. + +Below lists the effects of adding, deleting and updating of an +element/attribute within the `` element, and whether an change can be +done or can’t be done. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | X(no more than one is present) | Deleting it means delete (undeploy) all queues from running broker. | N/A `` | A new queue is deployed after reloading | The queue will be undeployed after reloading. | N/A attribute `name` | N/A | X | A queue with new name will be deployed and the queue with old name will be updeployed after reloading (see Note above). @@ -214,15 +250,17 @@ attribute `durable` | N/A | No effect unless starting broker | No effect unless #### `` -Changes to any `` elements will be reloaded to the running broker. +Changes to any `` elements will be reloaded to the running broker. -> *Note:* -> Once reloaded, new queues defined in the new changes will be deployed to the running -> broker. However existing queues won’t get undeployed even if the matching element is -> deleted/missing. Also new queue elements matching existing queues won’t get re-created – they remain unchanged. +> **Note:** +> +> Once reloaded, new queues defined in the new changes will be deployed to the +> running broker. However existing queues won’t get undeployed even if the +> matching element is deleted/missing. Also new queue elements matching +> existing queues won’t get re-created – they remain unchanged. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | A new jms queue will be deployed after reloading | No effect unless starting broker | No effect unless starting broker attribute `` | N/A | X | A jms queue of the new name will be deployed after reloading `` | X(no more than one is present) | No effect unless starting broker | No effect unless starting broker @@ -230,15 +268,16 @@ attribute `` | N/A | X | A jms queue of the new name will be deployed afte #### `` -Changes to any `` elements will be reloaded to the running broker. +Changes to any `` elements will be reloaded to the running broker. -> *Note:* -> Once reloaded, new topics defined in the new changes will be deployed to -> the running broker. However existing topics won’t get undeployed even if the +> **Note:** +> +> Once reloaded, new topics defined in the new changes will be deployed to the +> running broker. However existing topics won’t get undeployed even if the > matching element is deleted/missing. Also any `` elements matching > existing topics won’t get re-deployed – they remain unchanged. Operation | Add | Delete | Update -:--- | :--- | :--- | :--- +---|---|---|--- `` | A new jms topic will be deployed after reloading | No effect unless starting broker | No effect unless starting broker attribute `name` | N/A | X | A jms topic of the new name will be deployed after reloading diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index 4b45de9d43e..0064a776387 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -1,51 +1,29 @@ -Configuration Reference -======================= +# Configuration Reference This section is a quick index for looking up configuration. Click on the element name to go to the specific chapter. -Server Configuration -==================== +## Broker Configuration -broker.xml --------------------------- +### broker.xml -This is the main core server configuration file which contains the 'core' -element. -The 'core' element contains the main server configuration. +This is the main core server configuration file which contains the `core` +element. The `core` element contains the main server configuration. -# System properties +#### Modularising broker.xml -It is possible to use System properties to replace some of the configuration properties. If you define a System property starting with "brokerconfig." that will be passed along to Bean Utils and the configuration would be replaced. - -To define global-max-size=1000000 using a system property you would have to define this property, for example through java arguments: - -``` -java -Dbrokerconfig.globalMaxSize=1000000 -``` - -You can also change the prefix through the broker.xml by setting: - -``` -yourprefix -``` - -This is to help you customize artemis on embedded systems. - -# Modularising config into separate files. - -XML XInclude support is provided in the configuration as such if you wish to break your configuration out into separate files you can. +XML XInclude support is provided in `broker.xml` so that you can break your configuration out into separate files. To do this ensure the following is defined at the root configuration element. ``` - xmlns:xi="http://www.w3.org/2001/XInclude" +xmlns:xi="http://www.w3.org/2001/XInclude" ``` You can now define include tag's where you want to bring in xml configuration from another file: ``` - + ``` You should ensure xml elements in separated files should be namespaced correctly for example if address-settings element was separated, it should have the element namespace defined: @@ -55,241 +33,338 @@ You should ensure xml elements in separated files should be namespaced correctly ``` An example can of this feature can be seen in the test suites: -``` - ./artemis-server/src/test/resources/ConfigurationTest-xinclude-config.xml ``` -N.B. if you use xmllint to validate xml's against schema you should enable xinclude flag when running. +./artemis-server/src/test/resources/ConfigurationTest-xinclude-config.xml +``` +**Note:** if you use `xmllint` to validate the XML against the schema you should enable xinclude flag when running. ``` - --xinclude +--xinclude ``` For further information on XInclude see: -[https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/) +[https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/) + +### System properties + +It is possible to use System properties to replace some of the configuration properties. If you define a System property starting with "brokerconfig." that will be passed along to Bean Utils and the configuration would be replaced. + +To define global-max-size=1000000 using a system property you would have to define this property, for example through java arguments: + +``` +java -Dbrokerconfig.globalMaxSize=1000000 +``` + +You can also change the prefix through the `broker.xml` by setting: + +``` +yourprefix +``` + +This is to help you customize artemis on embedded systems. -# The core configuration +## The core configuration This describes the root of the XML configuration. You will see here also multiple sub-types listed. For example on the main config you will have bridges and at the [list of bridge](#bridge-type) type we will describe the properties for that configuration. +Name | Description | Default +---|---|--- +[acceptors](configuring-transports.md#acceptors) | a list of remoting acceptors | n/a +[acceptors.acceptor](configuring-transports.md#acceptors) | Each acceptor is composed for just an URL | n/a +[addresses](address-model.md#basic-address-configuration) | [a list of addresses](#address-type) | n/a +[address-settings](address-model.md#configuring-addresses-and-queues-via-address-settings) | [a list of address-setting](#address-setting-type) | n/a +[allow-failback](ha.md#failing-back-to-live-server)| Should stop backup on live restart. | `true` +[amqp-use-core-subscription-naming](amqp.md) | If true uses CORE queue naming convention for AMQP. | `false` +[async-connection-execution-enabled](connection-ttl.md) | If False delivery would be always asynchronous. | `true` +[bindings-directory](persistence.md) | The folder in use for the bindings folder | `data/bindings` +[bridges](core-bridges.md) | [a list of core bridges](#bridge-type) | n/a +[ha-policy](ha.md) | the HA policy of this server | none +[broadcast-groups](clusters.md#broadcast-groups) | [a list of broadcast-group](#broadcast-group-type) | n/a +[broker-plugins](broker-plugins.md) | [a list of broker-plugins](#broker-plugin-type) | n/a +[configuration-file-refresh-period](config-reload.md) | The frequency in milliseconds the configuration file is checked for changes | 5000 +[check-for-live-server](ha.md#data-replication)| Used for a live server to verify if there are other nodes with the same ID on the topology | n/a +[cluster-connections](clusters.md#configuring-cluster-connections) | [a list of cluster-connection](#cluster-connection-type) | n/a +[cluster-password](clusters.md) |Cluster password. It applies to all cluster configurations. | n/a +[cluster-user](clusters.md) |Cluster username. It applies to all cluster configurations. | n/a +[connection-ttl-override](connection-ttl.md) |if set, this will override how long (in ms) to keep a connection alive without receiving a ping. -1 disables this setting. | -1 +[connection-ttl-check-interval](connection-ttl.md) |how often (in ms) to check connections for ttl violation. | 2000 +[connectors.connector](configuring-transports.md) | The URL for the connector. This is a list | n/a +[create-bindings-dir](persistence.md) | true means that the server will create the bindings directory on start up. | `true` +[create-journal-dir](persistence.md)| true means that the journal directory will be created. | `true` +[discovery-groups](clusters.md#discovery-groups)| [a list of discovery-group](#discovery-group-type) | n/a +[disk-scan-period](paging.md#max-disk-usage) | The interval where the disk is scanned for percentual usage. | 5000 +[diverts](diverts.md) | [a list of diverts to use](#divert-type) | n/a +[global-max-size](paging.md#global-max-size) | The amount in bytes before all addresses are considered full. | Half of the JVM's `-Xmx` +[graceful-shutdown-enabled](graceful-shutdown.md)| true means that graceful shutdown is enabled. | `false` +[graceful-shutdown-timeout](graceful-shutdown.md)| Timeout on waiting for clients to disconnect before server shutdown. | -1 +[grouping-handler](message-grouping.md) | [a message grouping handler](#grouping-handler-type) | n/a +[id-cache-size](duplicate-detection.md#configuring-the-duplicate-id-cache) | The duplicate detection circular cache size. | 20000 +[jmx-domain](management.md#configuring-jmx) | the JMX domain used to registered MBeans in the MBeanServer. | `org.apache.activemq` +[jmx-use-broker-name](management.md#configuring-jmx) | whether or not to use the broker name in the JMX properties. | `true` +[jmx-management-enabled](management.md#configuring-jmx) | true means that the management API is available via JMX. | `true` +[journal-buffer-size](persistence.md#configuring-the-message-journal) | The size of the internal buffer on the journal in KB. | 490KB +[journal-buffer-timeout](persistence.md#configuring-the-message-journal) | The Flush timeout for the journal buffer | 500000 for ASYNCIO; 3333333 for NIO +[journal-compact-min-files](persistence.md#configuring-the-message-journal) | The minimal number of data files before we can start compacting. Setting this to 0 means compacting is disabled. | 10 +[journal-compact-percentage](persistence.md#configuring-the-message-journal) | The percentage of live data on which we consider compacting the journal. | 30 +[journal-directory](persistence.md#configuring-the-message-journal) | the directory to store the journal files in. | `data/journal` +[journal-file-size](persistence.md#configuring-the-message-journal) | the size (in bytes) of each journal file. | 10MB +[journal-lock-acquisition-timeout](persistence.md#configuring-the-message-journal) | how long (in ms) to wait to acquire a file lock on the journal. | -1 +[journal-max-io](persistence.md#configuring-the-message-journal) | the maximum number of write requests that can be in the ASYNCIO queue at any one time. | 4096 for ASYNCIO; 1 for NIO; ignored for MAPPED +[journal-file-open-timeout](persistence.md#configuring-the-message-journal) | the length of time in seconds to wait when opening a new journal file before timing out and failing. | 5 +[journal-min-files](persistence.md#configuring-the-message-journal) | how many journal files to pre-create. | 2 +[journal-pool-files](persistence.md#configuring-the-message-journal) | The upper threshold of the journal file pool, -1 means no Limit. The system will create as many files as needed however when reclaiming files it will shrink back to the `journal-pool-files` | -1 +[journal-sync-non-transactional](persistence.md#configuring-the-message-journal) | if true wait for non transaction data to be synced to the journal before returning response to client. | `true` +[journal-sync-transactional](persistence.md#configuring-the-message-journal)| if true wait for transaction data to be synchronized to the journal before returning response to client. | `true` +[journal-type](persistence.md#configuring-the-message-journal) | the type of journal to use. | `ASYNCIO` +[journal-datasync](persistence.md#configuring-the-message-journal) | It will use msync/fsync on journal operations. | `true` +[large-messages-directory](large-messages.md) | the directory to store large messages. | `data/largemessages` +log-delegate-factory-class-name | **deprecated** the name of the factory class to use for log delegation. | n/a +[management-address](management.md#configuring-management)| the name of the management address to send management messages to. | `activemq.management` +[management-notification-address](management.md#configuring-the-management-notification-address) | the name of the address that consumers bind to receive management notifications. | `activemq.notifications` +[mask-password](masking-passwords.md) | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. | `false` +[max-saved-replicated-journals-size](ha.md#data-replication) | This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. -1 Means no Limit; 0 don't keep a copy at all. | 2 +[max-disk-usage](paging.md#max-disk-usage) | The max percentage of data we should use from disks. The System will block while the disk is full. Disable by setting -1. | 100 +[memory-measure-interval](perf-tuning.md) | frequency to sample JVM memory in ms (or -1 to disable memory sampling). | -1 +[memory-warning-threshold](perf-tuning.md)| Percentage of available memory which will trigger a warning log. | 25 +[message-counter-enabled](management.md#message-counters) | true means that message counters are enabled. | `false` +[message-counter-max-day-history](management.md#message-counters)| how many days to keep message counter history. | 10 +[message-counter-sample-period](management.md#message-counters) | the sample period (in ms) to use for message counters. | 10000 +[message-expiry-scan-period](message-expiry.md#configuring-the-expiry-reaper-thread) | how often (in ms) to scan for expired messages. | 30000 +[message-expiry-thread-priority](message-expiry.md#configuring-the-expiry-reaper-thread)| the priority of the thread expiring messages. | 3 +name | node name; used in topology notifications if set. | n/a +[password-codec](masking-passwords.md) | the name of the class (and optional configuration properties) used to decode masked passwords. Only valid when `mask-password` is `true`. | n/a +[page-max-concurrent-io](paging.md) | The max number of concurrent reads allowed on paging. | 5 +[paging-directory](paging.md#configuration)| the directory to store paged messages in. | `data/paging` +[persist-delivery-count-before-delivery](undelivered-messages.md#delivery-count-persistence) | True means that the delivery count is persisted before delivery. False means that this only happens after a message has been cancelled. | `false` +[persistence-enabled](persistence.md#zero-persistence)| true means that the server will use the file based journal for persistence. | `true` +[persist-id-cache](duplicate-detection.md#configuring-the-duplicate-id-cache) | true means that ID's are persisted to the journal. | `true` +queues | **deprecated** [use addresses](#address-type) | n/a +[remoting-incoming-interceptors](intercepting-operations.md)| a list of <class-name/> elements with the names of classes to use for intercepting incoming remoting packets | n/a +[remoting-outgoing-interceptors](intercepting-operations.md)| a list of <class-name/> elements with the names of classes to use for intercepting outgoing remoting packets | n/a +[resolveProtocols]() | Use [ServiceLoader](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) to load protocol modules. | `true` +[resource-limit-settings](resource-limits.md) | [a list of resource-limits](#resource-limit-type) | n/a +[scheduled-thread-pool-max-size](thread-pooling.md#server-scheduled-thread-pool)| Maximum number of threads to use for the scheduled thread pool. | 5 +[security-enabled](security.md) | true means that security is enabled. | `true` +[security-invalidation-interval](security.md) | how long (in ms) to wait before invalidating the security cache. | 10000 +system-property-prefix | Prefix for replacing configuration settings using Bean Utils. | n/a +internal-naming-prefix | the prefix used when naming the internal queues and addresses required for implementing certain behaviours. | `$.activemq.internal` +[populate-validated-user](security.md#tracking-the-validated-user)| whether or not to add the name of the validated user to the messages that user sends. | `false` +[security-settings](security.md#role-based-security-for-addresses) | [a list of security-setting](#security-setting-type). | n/a +[thread-pool-max-size](thread-pooling.md#thread-management) | Maximum number of threads to use for the thread pool. -1 means 'no limits'. | 30 +[transaction-timeout](transaction-config.md) | how long (in ms) before a transaction can be removed from the resource manager after create time. | 300000 +[transaction-timeout-scan-period](transaction-config.md) | how often (in ms) to scan for timeout transactions. | 1000 +[wild-card-routing-enabled](wildcard-routing.md) | true means that the server supports wild card routing. | `true` +[network-check-NIC](network-isolation.md) | the NIC (Network Interface Controller) to be used on InetAddress.isReachable. | n/a +[network-check-URL-list](network-isolation.md) | the list of http URIs to be used to validate the network. | n/a +[network-check-list](network-isolation.md) | the list of pings to be used on ping or InetAddress.isReachable. | n/a +[network-check-period](network-isolation.md) | a frequency in milliseconds to how often we should check if the network is still up. | 10000 +[network-check-timeout](network-isolation.md) | a timeout used in milliseconds to be used on the ping. | 1000 +[network-check-ping-command](network-isolation.md) | the command used to oping IPV4 addresses. | n/a +[network-check-ping6-command](network-isolation.md) | the command used to oping IPV6 addresses. | n/a +[critical-analyzer](critical-analysis.md) | enable or disable the critical analysis. | `true` +[critical-analyzer-timeout](critical-analysis.md) | timeout used to do the critical analysis. | 120000 ms +[critical-analyzer-check-period](critical-analysis.md) | time used to check the response times. | 0.5 \* `critical-analyzer-timeout` +[critical-analyzer-policy](critical-analysis.md) | should the server log, be halted or shutdown upon failures. | `LOG` +resolve-protocols | if true then the broker will make use of any protocol managers that are in available on the classpath, otherwise only the core protocol will be available, unless in embedded mode where users can inject their own protocol managers. | `true` +[resource-limit-settings](resource-limits.md) | [a list of resource-limit](#resource-limit-type). | n/a +server-dump-interval | interval to log server specific information (e.g. memory usage etc). | -1 +store | the store type used by the server. | n/a +[wildcard-addresses](wildcard-syntax.md) | parameters to configure wildcard address matching format. | n/a + +## address-setting type + +Name | Description | Default +---|---|--- +[match](address-model.md) | The filter to apply to the setting | n/a +[dead-letter-address](undelivered-messages.md) | Dead letter address | n/a +[expiry-address](message-expiry.md) | Expired messages address | n/a +[expiry-delay](address-model.md) | Expiration time override; -1 don't override | -1 +[redelivery-delay](undelivered-messages.md) | Time to wait before redelivering a message | 0 +[redelivery-delay-multiplier](address-model.md) | Multiplier to apply to the `redelivery-delay` | 1.0 +[max-redelivery-delay](address-model.md) | Max value for the `redelivery-delay` | 10 \* `redelivery-delay` +[max-delivery-attempts](undelivered-messages.md)| Number of retries before dead letter address| 10 +[max-size-bytes](paging.md)| Max size a queue can be before invoking `address-full-policy` | -1 +[max-size-bytes-reject-threshold]() | Used with `BLOCK`, the max size an address can reach before messages are rejected; works in combination with `max-size-bytes` **for AMQP clients only**. | -1 +[page-size-bytes](paging.md) | Size of each file on page | 10485760 +[page-max-cache-size](paging.md) | Maximum number of files cached from paging | 5 +[address-full-policy](address-model.md)| What to do when a queue reaches `max-size-bytes` | `PAGE` +[message-counter-history-day-limit](address-model.md) | Days to keep message counter data | 0 +[last-value-queue](last-value-queues.md) | **deprecated** Queue is a last value queue; see `default-last-value-queue` instead | `false` +[default-last-value-queue](last-value-queues.md)| `last-value` value if none is set on the queue | `false` +[default-exclusive-queue](exclusive-queues.md) | `exclusive` value if none is set on the queue | `false` +[redistribution-delay](clusters.md) | Timeout before redistributing values after no consumers | -1 +[send-to-dla-on-no-route](address-model.md) | Forward messages to DLA when no queues subscribing | `false` +[slow-consumer-threshold](slow-consumers.md) | Min rate of msgs/sec consumed before a consumer is considered "slow" | -1 +[slow-consumer-policy](slow-consumers.md) | What to do when "slow" consumer is detected | `NOTIFY` +[slow-consumer-check-period](slow-consumers.md) | How often to check for "slow" consumers | 5 +[auto-create-jms-queues](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Create JMS queues automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-delete-jms-queues](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Delete JMS queues automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-create-jms-topics](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Create JMS topics automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-delete-jms-topics](address-model.md#configuring-addresses-and-queues-via-address-settings)| **deprecated** Delete JMS topics automatically; see `auto-create-queues` & `auto-create-addresses` | `true` +[auto-create-queues](address-model.md#configuring-addresses-and-queues-via-address-settings) | Create queues automatically | `true` +[auto-delete-queues](address-model.md#configuring-addresses-and-queues-via-address-settings) | Delete queues automatically | `true` +[config-delete-queues](config-reload.md)| How to deal with queues deleted from XML at runtime| `OFF` +[auto-create-addresses](address-model.md#configuring-addresses-and-queues-via-address-settings) | Create addresses automatically | `true` +[auto-delete-addresses](address-model.md#configuring-addresses-and-queues-via-address-settings) | Delete addresses automatically | `true` +[config-delete-addresses](config-reload.md) | How to deal with addresses deleted from XML at runtime | `OFF` +[management-browse-page-size]() | Number of messages a management resource can browse| 200 +[default-purge-on-no-consumers](address-model.md#non-durable-subscription-queue) | `purge-on-no-consumers` value if none is set on the queue | `false` +[default-max-consumers](address-model.md#shared-durable-subscription-queue-using-max-consumers) | `max-consumers` value if none is set on the queue | -1 +[default-queue-routing-type](address-model.md#routing-type) | Routing type for auto-created queues if the type can't be otherwise determined | `MULTICAST` +[default-address-routing-type](address-model.md#routing-type) | Routing type for auto-created addresses if the type can't be otherwise determined | `MULTICAST` + + +## bridge type + +Name | Description | Default +---|---|--- +[name ](core-bridges.md)| unique name | n/a +[queue-name](core-bridges.md) | name of queue that this bridge consumes from | n/a +[forwarding-address](core-bridges.md) | address to forward to. If omitted original address is used | n/a +[ha](core-bridges.md)| whether this bridge supports fail-over | `false` +[filter](core-bridges.md) | optional core filter expression | n/a +[transformer-class-name](core-bridges.md) | optional name of transformer class | n/a +[min-large-message-size](core-bridges.md) | Limit before message is considered large. | 100KB +[check-period](connection-ttl.md)| How often to check for [TTL](https://en.wikipedia.org/wiki/Time_to_live) violation. -1 means disabled. | 30000 +[connection-ttl](connection-ttl.md) | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") for the Bridge. This should be greater than the ping period. | 60000 +[retry-interval](core-bridges.md)| period (in ms) between successive retries. | 2000 +[retry-interval-multiplier](core-bridges.md) | multiplier to apply to successive retry intervals. | 1 +[max-retry-interval](core-bridges.md) | Limit to the retry-interval growth. | 2000 +[reconnect-attempts](core-bridges.md) | maximum number of retry attempts.| -1 (no limit) +[use-duplicate-detection](core-bridges.md)| forward duplicate detection headers? | `true` +[confirmation-window-size](core-bridges.md) | number of bytes before confirmations are sent. | 1MB +[producer-window-size](core-bridges.md)| Producer flow control size on the bridge. | -1 (disabled) +[user](core-bridges.md) | Username for the bridge, the default is the cluster username. | n/a +[password](core-bridges.md)| Password for the bridge, default is the cluster password. | n/a +[reconnect-attempts-same-node](core-bridges.md) | Number of retries before trying another node. | 10 + +## broadcast-group type + +Name | Type +---|--- +[name ](clusters.md) | unique name +[local-bind-address](clusters.md) | Local bind address that the datagram socket is bound to. +[local-bind-port](clusters.md) | Local port to which the datagram socket is bound to. +[group-address](clusters.md)| Multicast address to which the data will be broadcast. +[group-port](clusters.md)| UDP port number used for broadcasting. +[broadcast-period](clusters.md)| Period in milliseconds between consecutive broadcasts. Default=2000. +[jgroups-file](clusters.md) | Name of JGroups configuration file. +[jgroups-channel](clusters.md) | Name of JGroups Channel. +[connector-ref](clusters.md)| The `connector` to broadcast. + + +## cluster-connection type + +Name | Description | Default +---|---|--- +[name](clusters.md) | unique name | n/a +[address](clusters.md) | name of the address this cluster connection applies to | n/a +[connector-ref](clusters.md) | Name of the connector reference to use. | n/a +[check-period](connection-ttl.md) | The period (in milliseconds) used to check if the cluster connection has failed to receive pings from another server | 30000 +[connection-ttl](connection-ttl.md)| Timeout for TTL. | 60000 +[min-large-message-size](large-messages.md) | Messages larger than this are considered large-messages. | 100KB +[call-timeout](clusters.md) | Time(ms) before giving up on blocked calls. | 30000 +[retry-interval](clusters.md)| period (in ms) between successive retries. | 500 +[retry-interval-multiplier](clusters.md) | multiplier to apply to the retry-interval. | 1 +[max-retry-interval](clusters.md) | Maximum value for retry-interval. | 2000 +[reconnect-attempts](clusters.md) | How many attempts should be made to reconnect after failure. | -1 +[use-duplicate-detection](clusters.md)| should duplicate detection headers be inserted in forwarded messages? | `true` +[message-load-balancing](clusters.md) | how should messages be load balanced? | `OFF` +[max-hops](clusters.md)| maximum number of hops cluster topology is propagated. | 1 +[confirmation-window-size](client-reconnection.md#client-reconnection-and-session-reattachment)| The size (in bytes) of the window used for confirming data from the server connected to. | 1048576 +[producer-window-size](clusters.md)| Flow Control for the Cluster connection bridge. | -1 (disabled) +[call-failover-timeout](clusters.md#configuring-cluster-connections)| How long to wait for a reply if in the middle of a fail-over. -1 means wait forever. | -1 +[notification-interval](clusters.md) | how often the cluster connection will notify the cluster of its existence right after joining the cluster. | 1000 +[notification-attempts](clusters.md) | how many times this cluster connection will notify the cluster of its existence right after joining the cluster | 2 + + +## discovery-group type + Name | Description -:--- | :--- -[acceptors](configuring-transports.md "Understanding Acceptors") | a list of remoting acceptors -[acceptors.acceptor](configuring-transports.md "Understanding Acceptors") | Each acceptor is composed for just an URL -[address-settings](address-model.md "Configuring Addresses and Queues Via Address Settings") | [a list of address-setting](#address-setting-type) -[allow-failback](ha.md "Failing Back to live Server") | Should stop backup on live restart. default true -[amqp-use-core-subscription-naming](using-AMQP.md "Message Conversions") | If true uses CORE queue naming convention for AMQP. default false -[async-connection-execution-enabled](connection-ttl.md "Configuring Asynchronous Connection Execution") | If False delivery would be always asynchronous. default true -[bindings-directory](persistence.md "Configuring the bindings journal") | The folder in use for the bindings folder -[bridges](core-bridges.md "Core Bridges") | [a list of bridge](#bridge-type) -[broadcast-groups](clusters.md "Clusters") | [a list of broadcast-group](#broadcast-group-type) -[configuration-file-refresh-period](config-reload.md) | The frequency in milliseconds the configuration file is checked for changes (default 5000) -[check-for-live-server](ha.md) | Used for a live server to verify if there are other nodes with the same ID on the topology -[cluster-connections](clusters.md "Clusters") | [a list of cluster-connection](#cluster-connection-type) -[cluster-password](clusters.md "Clusters") | Cluster password. It applies to all cluster configurations. -[cluster-user](clusters.md "Clusters") | Cluster username. It applies to all cluster configurations. -[connection-ttl-override](connection-ttl.md) | if set, this will override how long (in ms) to keep a connection alive without receiving a ping. -1 disables this setting. Default -1 -[connection-ttl-check-period](connection-ttl.md) | how often (in ms) to check connections for ttl violation. Default 2000 -[connectors.connector](configuring-transports.md "Understanding Connectors") | The URL for the connector. This is a list -[create-bindings-dir](persistence.md "Configuring the bindings journal") | true means that the server will create the bindings directory on start up. Default=true -[create-journal-dir](persistence.md) | true means that the journal directory will be created. Default=true -[discovery-groups](clusters.md "Clusters") | [a list of discovery-group](#discovery-group-type) -[disk-scan-period](paging.md#max-disk-usage) | The interval where the disk is scanned for percentual usage. Default=5000 ms. -[diverts](diverts.md "Diverting and Splitting Message Flows") | [a list of diverts to use](#divert-type) -[global-max-size](paging.md#global-max-size) | The amount in bytes before all addresses are considered full. Default is half of the memory used by the JVM (-Xmx argument). -[graceful-shutdown-enabled](graceful-shutdown.md "Graceful Server Shutdown") | true means that graceful shutdown is enabled. Default=false -[graceful-shutdown-timeout](graceful-shutdown.md "Graceful Server Shutdown") | Timeout on waiting for clients to disconnect before server shutdown. Default=-1 -[grouping-handler](message-grouping.md "Message Grouping") | Message Group configuration -[id-cache-size](duplicate-detection.md "Configuring the Duplicate ID Cache") | The duplicate detection circular cache size. Default=20000 -[jmx-domain](management.md "Configuring JMX") | the JMX domain used to registered MBeans in the MBeanServer. Default=org.apache.activemq -[jmx-management-enabled](management.md "Configuring JMX") | true means that the management API is available via JMX. Default=true -[journal-buffer-size](persistence.md) | The size of the internal buffer on the journal in KB. Default=490 KiB -[journal-buffer-timeout](persistence.md) | The Flush timeout for the journal buffer -[journal-compact-min-files](persistence.md) | The minimal number of data files before we can start compacting. Setting this to 0 means compacting is disabled. Default=10 -[journal-compact-percentage](persistence.md) | The percentage of live data on which we consider compacting the journal. Default=30 -[journal-directory](persistence.md) | the directory to store the journal files in. Default=data/journal -[journal-file-size](persistence.md) | the size (in bytes) of each journal file. Default=10485760 (10 MB) -[journal-max-io](persistence.md#configuring-the-message-journal) | the maximum number of write requests that can be in the AIO queue at any one time. Default is 4096 for AIO and 1 for NIO, ignored for MAPPED. -[journal-min-files](persistence.md#configuring-the-message-journal) | how many journal files to pre-create. Default=2 -[journal-pool-files](persistence.md#configuring-the-message-journal) | The upper threshold of the journal file pool,-1 (default) means no Limit. The system will create as many files as needed however when reclaiming files it will shrink back to the `journal-pool-files` -[journal-sync-non-transactional](persistence.md) | if true wait for non transaction data to be synced to the journal before returning response to client. Default=true -[journal-sync-transactional](persistence.md) | if true wait for transaction data to be synchronized to the journal before returning response to client. Default=true -[journal-type](persistence.md) | the type of journal to use. Default=ASYNCIO -[journal-datasync](persistence.md) | It will use msync/fsync on journal operations. Default=true. -[large-messages-directory](large-messages.md "Configuring the server") | the directory to store large messages. Default=data/largemessages -[management-address](management.md "Configuring Core Management") | the name of the management address to send management messages to. Default=activemq.management -[management-notification-address](management.md "Configuring The Core Management Notification Address") | the name of the address that consumers bind to receive management notifications. Default=activemq.notifications -[mask-password](masking-passwords.md "Masking Passwords") | This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. Default=false -[max-saved-replicated-journals-size](ha.md#data-replication) | This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. -1 Means no Limit, 0 don't keep a copy at all, Default=2 -[max-disk-usage](paging.md#max-disk-usage) | The max percentage of data we should use from disks. The System will block while the disk is full. Disable by setting -1. Default=100 -[memory-measure-interval](perf-tuning.md) | frequency to sample JVM memory in ms (or -1 to disable memory sampling). Default=-1 -[memory-warning-threshold](perf-tuning.md) | Percentage of available memory which will trigger a warning log. Default=25 -[message-counter-enabled](management.md "Configuring Message Counters") | true means that message counters are enabled. Default=false -[message-counter-max-day-history](management.md "Configuring Message Counters") | how many days to keep message counter history. Default=10 (days) -[message-counter-sample-period](management.md "Configuring Message Counters") | the sample period (in ms) to use for message counters. Default=10000 -[message-expiry-scan-period](message-expiry.md "Configuring The Expiry Reaper Thread") | how often (in ms) to scan for expired messages. Default=30000 -[message-expiry-thread-priority](message-expiry.md "Configuring The Expiry Reaper Thread") | the priority of the thread expiring messages. Default=3 -[password-codec](masking-passwords.md "Masking Passwords") | the name of the class (and optional configuration properties) used to decode masked passwords. Only valid when `mask-password` is `true`. Default=empty -[page-max-concurrent-io](paging.md "Paging Mode") | The max number of concurrent reads allowed on paging. Default=5 -[paging-directory](paging.md "Configuration") | the directory to store paged messages in. Default=data/paging -[persist-delivery-count-before-delivery](undelivered-messages.md "Delivery Count Persistence") | True means that the delivery count is persisted before delivery. False means that this only happens after a message has been cancelled. Default=false -[persistence-enabled](persistence.md "Configuring ActiveMQ Artemis for Zero Persistence") | true means that the server will use the file based journal for persistence. Default=true -[persist-id-cache](duplicate-detection.md "Configuring the Duplicate ID Cache") | true means that ID's are persisted to the journal. Default=true -[queues](address-model.md "Predefined Queues") | [a list of queue to be created](#queue-type) -[remoting-incoming-interceptors](intercepting-operations.md "Intercepting Operations") | A list of interceptor -[resolveProtocols]() | Use [ServiceLoader](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) to load protocol modules. Default=true -[scheduled-thread-pool-max-size](thread-pooling.md#server-scheduled-thread-pool "Server Scheduled Thread Pool")| Maximum number of threads to use for the scheduled thread pool. Default=5 -[security-enabled](security.md "Security") | true means that security is enabled. Default=true -[security-invalidation-interval](security.md "Security") | how long (in ms) to wait before invalidating the security cache. Default=10000 -system-property-prefix | Prefix for replacing configuration settings using Bean Utils. -[populate-validated-user](security.md "Security") | whether or not to add the name of the validated user to the messages that user sends. Default=false -[security-settings](security.md "Role based security for addresses") | [a list of security-setting](#security-setting-type) -[thread-pool-max-size](thread-pooling.md "Server Scheduled Thread Pool") | Maximum number of threads to use for the thread pool. -1 means 'no limits'.. Default=30 -[transaction-timeout](transaction-config.md "Resource Manager Configuration") | how long (in ms) before a transaction can be removed from the resource manager after create time. Default=300000 -[transaction-timeout-scan-period](transaction-config.md "Resource Manager Configuration") | how often (in ms) to scan for timeout transactions. Default=1000 -[wild-card-routing-enabled](wildcard-routing.md "Routing Messages With Wild Cards") | true means that the server supports wild card routing. Default=true -[network-check-NIC](network-isolation.md) | The NIC (Network Interface Controller) to be used on InetAddress.isReachable -[network-check-URL](network-isolation.md) | The list of http URIs to be used to validate the network -[network-check-list](network-isolation.md) | The list of pings to be used on ping or InetAddress.isReachable -[network-check-ping-command](network-isolation.md) | The command used to oping IPV4 addresses -[network-check-ping6-command](network-isolation.md) | The command used to oping IPV6 addresses -[critical-analyzer](critical-analysis.md) | Enable or disable the critical analysis (default true) -[critical-analyzer-timeout](critical-analysis.md) | Timeout used to do the critical analysis (default 120000 milliseconds) -[critical-analyzer-check-period](critical-analysis.md) | Time used to check the response times (default half of critical-analyzer-timeout) -[critical-analyzer-policy](critical-analysis.md) | Should the server log, be halted or shutdown upon failures (default `LOG`) - - -#address-setting type +---|--- +[name](clusters.md)| unique name +[group-address](clusters.md)| Multicast IP address of the group to listen on +[group-port](clusters.md)| UDP port number of the multi cast group +[jgroups-file](clusters.md) | Name of a JGroups configuration file. If specified, the server uses JGroups for discovery. +[jgroups-channel](clusters.md) | Name of a JGroups Channel. If specified, the server uses the named channel for discovery. +[refresh-timeout]()| Period the discovery group waits after receiving the last broadcast from a particular server before removing that servers connector pair entry from its list. Default=10000 +[local-bind-address](clusters.md) | local bind address that the datagram socket is bound to +[local-bind-port](clusters.md) | local port to which the datagram socket is bound to. Default=-1 +initial-wait-timeout | time to wait for an initial broadcast to give us at least one node in the cluster. Default=10000 + +## divert type Name | Description -:--- | :--- -[match ](address-model.md "Configuring Queues Via Address Settings") | The filter to apply to the setting -[dead-letter-address](undelivered-messages.md "Configuring Dead Letter Addresses") | dead letter address -[expiry-address](message-expiry.md "Configuring Expiry Addresses") | expired messages address -[expiry-delay](address-model.md "Configuring Queues Via Address Settings") | expiration time override, -1 don't override with default=-1 -[redelivery-delay](undelivered-messages.md "Configuring Delayed Redelivery") | time to redeliver a message (in ms) with default=0 -[redelivery-delay-multiplier](address-model.md "Configuring Queues Via Address Settings") | multiplier to apply to the "redelivery-delay" -[max-redelivery-delay](address-model.md "Configuring Queues Via Address Settings") | Max value for the redelivery-delay -[max-delivery-attempts](undelivered-messages.md "Configuring Dead Letter Addresses") | Number of retries before dead letter address, default=10 -[max-size-bytes](paging.md "Paging") | Limit before paging. -1 = infinite -[page-size-bytes](paging.md "Paging") | Size of each file on page, default=10485760 -[page-max-cache-size](paging.md "Paging") | Maximum number of files cached from paging default=5 -[address-full-policy](address-model.md "Configuring Queues Via Address Settings") | Model to chose after queue full -[message-counter-history-day-limit](address-model.md "Configuring Queues Via Address Settings") | Days to keep in history -[last-value-queue](last-value-queues.md "Last-Value Queues") | Queue is a last value queue, default=false -[redistribution-delay](clusters.md "Clusters") | Timeout before redistributing values after no consumers. default=-1 -[send-to-dla-on-no-route](address-model.md "Configuring Queues Via Address Settings") | Forward messages to DLA when no queues subscribing. default=false - - -#bridge type +---|--- +[name](diverts.md) | unique name +[transformer-class-name](diverts.md) | an optional class name of a transformer +[exclusive](diverts.md) | whether this is an exclusive divert. Default=false +[routing-name](diverts.md) | the routing name for the divert +[address](diverts.md) | the address this divert will divert from +[forwarding-address](diverts.md) | the forwarding address for the divert +[filter](diverts.md) | optional core filter expression + + +## address type Name | Description -:--- | :--- -[name ](core-bridges.md "Core Bridges") | unique name -[queue-name](core-bridges.md "Core Bridges") | name of queue that this bridge consumes from -[forwarding-address](core-bridges.md "Core Bridges") | address to forward to. If omitted original address is used -[ha](core-bridges.md "Core Bridges") | whether this bridge supports fail-over -[filter](core-bridges.md "Core Bridges") | optional core filter expression -[transformer-class-name](core-bridges.md "Core Bridges") | optional name of transformer class -[min-large-message-size](core-bridges.md "Core Bridges") | Limit before message is considered large. default 100KB -[check-period](connection-ttl.md "Detecting Dead Connections") | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") check period for the bridge. -1 means disabled. default 30000 (ms) -[connection-ttl](connection-ttl.md "Detecting Dead Connections") | [TTL](https://en.wikipedia.org/wiki/Time_to_live "Time to Live") for the Bridge. This should be greater than the ping period. default 60000 (ms) -[retry-interval](core-bridges.md "Core Bridges") | period (in ms) between successive retries. default 2000 -[retry-interval-multiplier](core-bridges.md "Core Bridges") | multiplier to apply to successive retry intervals. default 1 -[max-retry-interval](core-bridges.md "Core Bridges") | Limit to the retry-interval growth. default 2000 -[reconnect-attempts](core-bridges.md "Core Bridges") | maximum number of retry attempts, -1 means 'no limits'. default -1 -[use-duplicate-detection](core-bridges.md "Core Bridges") | forward duplicate detection headers?. default true -[confirmation-window-size](core-bridges.md "Core Bridges") | number of bytes before confirmations are sent. default 1MB -[producer-window-size](core-bridges.md "Core Bridges") | Producer flow control size on the bridge. Default -1 (disabled) -[user](core-bridges.md "Core Bridges") | Username for the bridge, the default is the cluster username -[password](core-bridges.md "Core Bridges") | Password for the bridge, default is the cluster password -[reconnect-attempts-same-node](core-bridges.md "Core Bridges") | Number of retries before trying another node. default 10 - -# broadcast-group type +---|--- +name | unique name | n/a +[anycast](address-model.md)| list of anycast [queues](#queue-type) +[multicast](address-model.md) | list of multicast [queues](#queue-type) -Name | Type -:--- | :--- -[name ](clusters.md "Clusters") | unique name -[local-bind-address](clusters.md "Clusters") | local bind address that the datagram socket is bound to -[local-bind-port](clusters.md "Clusters") | local port to which the datagram socket is bound to -[group-address](clusters.md "Clusters") | multicast address to which the data will be broadcast -[group-port](clusters.md "Clusters") | UDP port number used for broadcasting -[broadcast-period](clusters.md "Clusters") | period in milliseconds between consecutive broadcasts. default 2000 -[jgroups-file](clusters.md) | Name of JGroups configuration file -[jgroups-channel](clusters.md) | Name of JGroups Channel -[connector-ref](clusters.md "Clusters") | +## queue type -#cluster-connection type +Name | Description | Default +---|---|--- +name | unique name | n/a +filter | optional core filter expression | n/a +durable | whether the queue is durable (persistent). | `true` +user | the name of the user to associate with the creation of the queue | n/a +[max-consumers](address-model.md#shared-durable-subscription-queue-using-max-consumers) | the max number of consumers allowed on this queue | -1 (no max) +[purge-on-no-consumers](address-model.md#non-durable-subscription-queue) | whether or not to delete all messages and prevent routing when no consumers are connected | `false` +[exclusive](exclusive-queues.md) | only deliver messages to one of the connected consumers | `false` +[last-value](last-value-queues.md) | use last-value semantics | `false` -Name | Description -:--- | :--- -[name](clusters.md "Clusters") | unique name -[address](clusters.md "Clusters") | name of the address this cluster connection applies to -[connector-ref](clusters.md "Clusters") | Name of the connector reference to use. -[check-period](connection-ttl.md "Detecting Dead Connections") | The period (in milliseconds) used to check if the cluster connection has failed to receive pings from another server with default = 30000 -[connection-ttl](connection-ttl.md "Detecting Dead Connections") | Timeout for TTL. Default 60000 -[min-large-message-size](large-messages.md "Large Messages") | Messages larger than this are considered large-messages, default=100KB -[call-timeout](clusters.md "Clusters") | Time(ms) before giving up on blocked calls. Default=30000 -[retry-interval](clusters.md "Clusters") | period (in ms) between successive retries. Default=500 -[retry-interval-multiplier](clusters.md "Clusters") | multiplier to apply to the retry-interval. Default=1 -[max-retry-interval](clusters.md "Clusters") | Maximum value for retry-interval. Default=2000 -[reconnect-attempts](clusters.md "Clusters") | How many attempts should be made to reconnect after failure. Default=-1 -[use-duplicate-detection](clusters.md "Clusters") | should duplicate detection headers be inserted in forwarded messages?. Default=true -[message-load-balancing](clusters.md "Clusters") | how should messages be load balanced? Default=OFF -[max-hops](clusters.md "Clusters") | maximum number of hops cluster topology is propagated. Default=1 -[confirmation-window-size](client-reconnection.md "Client Reconnection and Session Reattachment")| The size (in bytes) of the window used for confirming data from the server connected to. Default 1048576 -[producer-window-size](clusters.md "Clusters") | Flow Control for the Cluster connection bridge. Default -1 (disabled) -[call-failover-timeout](clusters.md "Configuring Cluster Connections") | How long to wait for a reply if in the middle of a fail-over. -1 means wait forever. Default -1 -[notification-interval](clusters.md "Clusters") | how often the cluster connection will notify the cluster of its existence right after joining the cluster. Default 1000 -[notification-attempts](clusters.md "Clusters") | how many times this cluster connection will notify the cluster of its existence right after joining the cluster Default 2 - - -#discovery-group type -Name | Description -:--- | :--- -[name](clusters.md "Clusters") | unique name -[group-address](clusters.md "Clusters") | Multicast IP address of the group to listen on -[group-port](clusters.md "Clusters") | UDP port number of the multi cast group -[jgroups-file](clusters.md) | Name of a JGroups configuration file. If specified, the server uses JGroups for discovery. -[jgroups-channel](clusters.md) | Name of a JGroups Channel. If specified, the server uses the named channel for discovery. -[refresh-timeout]() | Period the discovery group waits after receiving the last broadcast from a particular server before removing that servers connector pair entry from its list. Default=10000 -[local-bind-address](clusters.md "Clusters") | local bind address that the datagram socket is bound to -[local-bind-port](clusters.md "Clusters") | local port to which the datagram socket is bound to. Default=-1 -[initial-wait-timeout]() | time to wait for an initial broadcast to give us at least one node in the cluster. Default=10000 - -#divert type +## security-setting type Name | Description -:--- | :--- -[name](diverts.md "Diverting and Splitting Message Flows") | unique name -[transformer-class-name](diverts.md "Diverting and Splitting Message Flows") | an optional class name of a transformer -[exclusive](diverts.md "Diverting and Splitting Message Flows") | whether this is an exclusive divert. Default=false -[routing-name](diverts.md "Diverting and Splitting Message Flows") | the routing name for the divert -[address](diverts.md "Diverting and Splitting Message Flows") | the address this divert will divert from -[forwarding-address](diverts.md "Diverting and Splitting Message Flows") | the forwarding address for the divert -[filter](diverts.md "Diverting and Splitting Message Flows")| optional core filter expression +---|--- +[match](security.md)| [address expression](wildcard-syntax.md) +[permission](security.md) | +[permission.type](security.md) | the type of permission +[permission.roles](security.md) | a comma-separated list of roles to apply the permission to -#queue type +## broker-plugin type Name | Description -:--- | :--- -[name ](address-model.md "Predefined Queues") | unique name -[address](address-model.md "Predefined Queues") | address for the queue -[filter](address-model.md "Predefined Queues") | optional core filter expression -[durable](address-model.md "Predefined Queues") | whether the queue is durable (persistent). Default=true +---|--- +[property](broker-plugins.md#registering-a-plugin)| properties to configure a plugin +[class-name](broker-plugins.md#registering-a-plugin) | the name of the broker plugin class to instantiate -#security-setting type +## resource-limit type -Name | Description -:--- | :--- -[match ](security.md "Role based security for addresses") | [address expression](wildcard-syntax.md) -[permission](security.md "Role based security for addresses") | -[permission.type ](security.md "Role based security for addresses") | the type of permission -[permission.roles ](security.md "Role based security for addresses") | a comma-separated list of roles to apply the permission to +Name | Description | Default +---|---|--- +[match](resource-limits.md#configuring-limits-via-resource-limit-settings)| the name of the user to whom the limits should be applied | n/a +[max-connections](resource-limits.md#configuring-limits-via-resource-limit-settings)| how many connections are allowed by the matched user | -1 (no max) +[max-queues](resource-limits.md#configuring-limits-via-resource-limit-settings)| how many queues can be created by the matched user | -1 (no max) + + +## grouping-handler type +Name | Description | Default +---|---|--- +[name](message-grouping.md#clustered-grouping) | A unique name | n/a +[type](message-grouping.md#clustered-grouping) | `LOCAL` or `REMOTE` | n/a +[address](message-grouping.md#clustered-grouping) | A reference to a `cluster-connection` `address` | n/a +[timeout](message-grouping.md#clustered-grouping) | How long to wait for a decision | 5000 +[group-timeout](message-grouping.md#clustered-grouping) | How long a group binding will be used. | -1 (disabled) +[reaper-period](message-grouping.md#clustered-grouping) | How often the reaper will be run to check for timed out group bindings. Only valid for `LOCAL` handlers. | 30000 \ No newline at end of file diff --git a/docs/user-manual/en/configuring-transports.md b/docs/user-manual/en/configuring-transports.md index 47dfc7b6322..ee3a0471c6a 100644 --- a/docs/user-manual/en/configuring-transports.md +++ b/docs/user-manual/en/configuring-transports.md @@ -1,43 +1,39 @@ # Configuring the Transport -In this chapter we'll describe the concepts required for understanding -Apache ActiveMQ Artemis transports and where and how they're configured. +In this chapter we'll describe the concepts required for understanding Apache +ActiveMQ Artemis transports and where and how they're configured. ## Acceptors One of the most important concepts in Apache ActiveMQ Artemis transports is the -*acceptor*. Let's dive straight in and take a look at an acceptor -defined in xml in the configuration file `broker.xml`. +*acceptor*. Let's dive straight in and take a look at an acceptor defined in +xml in the configuration file `broker.xml`. ```xml - - tcp://localhost:61617 - +tcp://localhost:61617 ``` -Acceptors are always defined inside an `acceptors` element. There can be -one or more acceptors defined in the `acceptors` element. There's no -upper limit to the number of acceptors per server. +Acceptors are always defined inside an `acceptors` element. There can be one or +more acceptors defined in the `acceptors` element. There's no upper limit to +the number of acceptors per server. -Each acceptor defines a way in which connections can be made to the -Apache ActiveMQ Artemis server. +Each acceptor defines a way in which connections can be made to the Apache +ActiveMQ Artemis server. In the above example we're defining an acceptor that uses -[Netty](https://netty.io/) to listen for connections at port -`61617`. - -The `acceptor` element contains a `URL` that defines the kind of Acceptor -to create along with its configuration. The `schema` part of the `URL` -defines the Acceptor type which can either be `tcp` or `vm` which is -`Netty` or an In VM Acceptor respectively. For `Netty` the host and the -port of the `URL` define what host and port the `acceptor` will bind to. For -In VM the `Authority` part of the `URL` defines a unique server id. - -The `acceptor` can also be configured with a set of key=value pairs -used to configure the specific transport, the set of -valid key=value pairs depends on the specific transport be used and are -passed straight through to the underlying transport. These are set on the -`URL` as part of the query, like so: +[Netty](https://netty.io/) to listen for connections at port `61617`. + +The `acceptor` element contains a `URL` that defines the kind of Acceptor to +create along with its configuration. The `schema` part of the `URL` defines the +Acceptor type which can either be `tcp` or `vm` which is `Netty` or an In VM +Acceptor respectively. For `Netty` the host and the port of the `URL` define +what host and port the `acceptor` will bind to. For In VM the `Authority` part +of the `URL` defines a unique server id. + +The `acceptor` can also be configured with a set of key=value pairs used to +configure the specific transport, the set of valid key=value pairs depends on +the specific transport be used and are passed straight through to the +underlying transport. These are set on the `URL` as part of the query, like so: ```xml tcp://localhost:61617?sslEnabled=true&keyStorePath=/path @@ -45,43 +41,41 @@ passed straight through to the underlying transport. These are set on the ## Connectors -Whereas acceptors are used on the server to define how we accept -connections, connectors are used to define how to connect to a server. +Whereas acceptors are used on the server to define how we accept connections, +connectors are used to define how to connect to a server. Let's look at a connector defined in our `broker.xml` file: ```xml - - tcp://localhost:61617 - +tcp://localhost:61617 ``` -Connectors can be defined inside a `connectors` element. There can be -one or more connectors defined in the `connectors` element. There's no -upper limit to the number of connectors per server. +Connectors can be defined inside a `connectors` element. There can be one or +more connectors defined in the `connectors` element. There's no upper limit to +the number of connectors per server. A `connector` is used when the server acts as a client itself, e.g.: -- When one server is bridged to another -- When a server takes part in a cluster +- When one server is bridged to another +- When a server takes part in a cluster In these cases the server needs to know how to connect to other servers. That's defined by `connectors`. ## Configuring the Transport Directly from the Client -How do we configure a core `ClientSessionFactory` with the information -that it needs to connect with a server? +How do we configure a core `ClientSessionFactory` with the information that it +needs to connect with a server? Connectors are also used indirectly when configuring a core -`ClientSessionFactory` to directly talk to a server. Although in this -case there's no need to define such a connector in the server side -configuration, instead we just specify the appropriate URI. +`ClientSessionFactory` to directly talk to a server. Although in this case +there's no need to define such a connector in the server side configuration, +instead we just specify the appropriate URI. -Here's an example of creating a `ClientSessionFactory` which will -connect directly to the acceptor we defined earlier in this chapter, it -uses the standard Netty TCP transport and will try and connect on port -61617 to localhost (default): +Here's an example of creating a `ClientSessionFactory` which will connect +directly to the acceptor we defined earlier in this chapter, it uses the +standard Netty TCP transport and will try and connect on port 61617 to +localhost (default): ```java ServerLocator locator = ActiveMQClient.createServerLocator("tcp://localhost:61617"); @@ -91,8 +85,8 @@ ClientSessionFactory sessionFactory = locator.createClientSessionFactory(); ClientSession session = sessionFactory.createSession(...); ``` -Similarly, if you're using JMS, you can configure the JMS connection -factory directly on the client side: +Similarly, if you're using JMS, you can configure the JMS connection factory +directly on the client side: ```java ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61617"); @@ -103,8 +97,7 @@ Connection jmsConnection = connectionFactory.createConnection(); ## Configuring the Netty transport Out of the box, Apache ActiveMQ Artemis currently uses -[Netty](https://netty.io/), a high performance low level -network library. +[Netty](https://netty.io/), a high performance low level network library. Our Netty transport can be configured in several different ways; to use straightforward TCP sockets, SSL, or to tunnel over HTTP or HTTPS.. @@ -113,14 +106,14 @@ We believe this caters for the vast majority of transport requirements. ### Single Port Support -Apache ActiveMQ Artemis supports using a single port for all -protocols, Apache ActiveMQ Artemis will automatically detect which protocol is being -used CORE, AMQP, STOMP or OPENWIRE and use the appropriate Apache ActiveMQ Artemis -handler. It will also detect whether protocols such as HTTP or Web -Sockets are being used and also use the appropriate decoders +Apache ActiveMQ Artemis supports using a single port for all protocols, Apache +ActiveMQ Artemis will automatically detect which protocol is being used CORE, +AMQP, STOMP or OPENWIRE and use the appropriate Apache ActiveMQ Artemis +handler. It will also detect whether protocols such as HTTP or Web Sockets are +being used and also use the appropriate decoders -It is possible to limit which protocols are supported by using the -`protocols` parameter on the Acceptor like so: +It is possible to limit which protocols are supported by using the `protocols` +parameter on the Acceptor like so: ```xml tcp://localhost:61617?protocols=CORE,AMQP @@ -129,348 +122,344 @@ It is possible to limit which protocols are supported by using the ### Configuring Netty TCP Netty TCP is a simple unencrypted TCP sockets based transport. If you're -running connections across an untrusted network please bear in -mind this transport is unencrypted. You may want to look at the SSL or -HTTPS configurations. +running connections across an untrusted network please bear in mind this +transport is unencrypted. You may want to look at the SSL or HTTPS +configurations. -With the Netty TCP transport all connections are initiated from the -client side (i.e. the server does not initiate any connections to the -client). This works well with firewall policies that typically only allow -connections to be initiated in one direction. +With the Netty TCP transport all connections are initiated from the client side +(i.e. the server does not initiate any connections to the client). This works +well with firewall policies that typically only allow connections to be +initiated in one direction. All the valid keys for the `tcp` URL scheme used for Netty are defined in the -class `org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants`. -Most parameters can be used either with acceptors or connectors, some only -work with acceptors. The following parameters can be used to configure -Netty for simple TCP: +class +`org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants`. +Most parameters can be used either with acceptors or connectors, some only work +with acceptors. The following parameters can be used to configure Netty for +simple TCP: -> **Note** +> **Note:** > -> The `host` and `port` parameters are only used in the core API, in -> XML configuration these are set in the URI host and port. - -- `host`. This specifies the host name or IP address to connect to - (when configuring a connector) or to listen on (when configuring an - acceptor). The default value for this property is `localhost`. When - configuring acceptors, multiple hosts or IP addresses can be - specified by separating them with commas. It is also possible to - specify `0.0.0.0` to accept connection from all the host's network - interfaces. It's not valid to specify multiple addresses when - specifying the host for a connector; a connector makes a connection - to one specific address. - - > **Note** - > - > Don't forget to specify a host name or IP address! If you want - > your server able to accept connections from other nodes you must - > specify a hostname or IP address at which the acceptor will bind - > and listen for incoming connections. The default is localhost - > which of course is not accessible from remote nodes! - -- `port`. This specified the port to connect to (when configuring a - connector) or to listen on (when configuring an acceptor). The - default value for this property is `61616`. - -- `tcpNoDelay`. If this is `true` then [Nagle's - algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) will be - disabled. This is a [Java (client) socket - option](https://docs.oracle.com/javase/8/docs/technotes/guides/net/socketOpt.html). - The default value for this property is `true`. - -- `tcpSendBufferSize`. This parameter determines the size of the - TCP send buffer in bytes. The default value for this property is - `32768` bytes (32KiB). - - TCP buffer sizes should be tuned according to the bandwidth and - latency of your network. Here's a good link that explains the theory - behind [this](http://www-didc.lbl.gov/TCP-tuning/). - - In summary TCP send/receive buffer sizes should be calculated as: - - buffer_size = bandwidth * RTT. - - Where bandwidth is in *bytes per second* and network round trip time - (RTT) is in seconds. RTT can be easily measured using the `ping` - utility. - - For fast networks you may want to increase the buffer sizes from the - defaults. - -- `tcpReceiveBufferSize`. This parameter determines the size of the - TCP receive buffer in bytes. The default value for this property is - `32768` bytes (32KiB). +> The `host` and `port` parameters are only used in the core API, in XML +> configuration these are set in the URI host and port. + +- `host`. This specifies the host name or IP address to connect to (when + configuring a connector) or to listen on (when configuring an acceptor). The + default value for this property is `localhost`. When configuring acceptors, + multiple hosts or IP addresses can be specified by separating them with commas. + It is also possible to specify `0.0.0.0` to accept connection from all the + host's network interfaces. It's not valid to specify multiple addresses when + specifying the host for a connector; a connector makes a connection to one + specific address. + + > **Note:** + > + > Don't forget to specify a host name or IP address! If you want your server + > able to accept connections from other nodes you must specify a hostname or + > IP address at which the acceptor will bind and listen for incoming + > connections. The default is localhost which of course is not accessible + > from remote nodes! + +- `port`. This specified the port to connect to (when configuring a connector) + or to listen on (when configuring an acceptor). The default value for this + property is `61616`. + +- `tcpNoDelay`. If this is `true` then [Nagle's + algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm) will be + disabled. This is a [Java (client) socket + option](https://docs.oracle.com/javase/8/docs/technotes/guides/net/socketOpt.html). + The default value for this property is `true`. + +- `tcpSendBufferSize`. This parameter determines the size of the TCP send + buffer in bytes. The default value for this property is `32768` bytes + (32KiB). + + TCP buffer sizes should be tuned according to the bandwidth and latency of + your network. Here's a good link that explains the theory behind + [this](http://www-didc.lbl.gov/TCP-tuning/). + + In summary TCP send/receive buffer sizes should be calculated as: + + buffer_size = bandwidth * RTT. + + Where bandwidth is in *bytes per second* and network round trip time (RTT) is + in seconds. RTT can be easily measured using the `ping` utility. + + For fast networks you may want to increase the buffer sizes from the + defaults. + +- `tcpReceiveBufferSize`. This parameter determines the size of the TCP receive + buffer in bytes. The default value for this property is `32768` bytes + (32KiB). -- `writeBufferLowWaterMark`. This parameter determines the low water mark of - the Netty write buffer. Once the number of bytes queued in the write buffer exceeded - the high water mark and then dropped down below this value, Netty's channel - will start to be writable again. The default value for this property is - `32768` bytes (32KiB). +- `writeBufferLowWaterMark`. This parameter determines the low water mark of + the Netty write buffer. Once the number of bytes queued in the write buffer + exceeded the high water mark and then dropped down below this value, Netty's + channel will start to be writable again. The default value for this property is + `32768` bytes (32KiB). -- `writeBufferHighWaterMark`. This parameter determines the high water mark of - the Netty write buffer. If the number of bytes queued in the write buffer exceeds - this value, Netty's channel will start to be not writable. The default value for - this property is `131072` bytes (128KiB). - -- `batchDelay`. Before writing packets to the transport, Apache ActiveMQ Artemis can - be configured to batch up writes for a maximum of `batchDelay` - milliseconds. This can increase overall throughput for very small - messages. It does so at the expense of an increase in average - latency for message transfer. The default value for this property is - `0` ms. - -- `directDeliver`. When a message arrives on the server and is - delivered to waiting consumers, by default, the delivery is done on - the same thread as that on which the message arrived. This gives - good latency in environments with relatively small messages and a - small number of consumers, but at the cost of overall throughput and - scalability - especially on multi-core machines. If you want the - lowest latency and a possible reduction in throughput then you can - use the default value for `directDeliver` (i.e. `true`). If you are - willing to take some small extra hit on latency but want the highest - throughput set `directDeliver` to `false`. - -- `nioRemotingThreads` This is deprecated. It is replaced by `remotingThreads`, - if you are using this please update your configuration - -- `remotingThreads`. Apache ActiveMQ Artemis will, - by default, use a number of threads equal to three times the number - of cores (or hyper-threads) as reported by - `Runtime.getRuntime().availableProcessors()` for processing incoming - packets. If you want to override this value, you can set the number - of threads by specifying this parameter. The default value for this - parameter is `-1` which means use the value from - `Runtime.getRuntime().availableProcessors()` \* 3. - -- `localAddress`. When configured a Netty Connector it is possible to - specify which local address the client will use when connecting to - the remote address. This is typically used in the Application Server - or when running Embedded to control which address is used for - outbound connections. If the local-address is not set then the - connector will use any local address available - -- `localPort`. When configured a Netty Connector it is possible to - specify which local port the client will use when connecting to the - remote address. This is typically used in the Application Server or - when running Embedded to control which port is used for outbound - connections. If the local-port default is used, which is 0, then the - connector will let the system pick up an ephemeral port. valid ports - are 0 to 65535 - -- `connectionsAllowed`. This is only valid for acceptors. It limits the - number of connections which the acceptor will allow. When this limit - is reached a DEBUG level message is issued to the log, and the connection - is refused. The type of client in use will determine what happens when - the connection is refused. In the case of a `core` client, it will - result in a `org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException`. - -- `handshake-timeout`. Prevents an unauthorised client opening a large - number of connections and just keeping them open. As connections each - require a file handle this consumes resources that are then unavailable - to other clients. Once the connection is authenticated, the usual rules - can be enforced regarding resource consumption. Default value is set to - 10 seconds. Each integer is valid value. When set value to zero or - negative integer this feature is turned off. Changing value needs - to restart server to take effect. +- `writeBufferHighWaterMark`. This parameter determines the high water mark of + the Netty write buffer. If the number of bytes queued in the write buffer + exceeds this value, Netty's channel will start to be not writable. The default + value for this property is `131072` bytes (128KiB). + +- `batchDelay`. Before writing packets to the transport, Apache ActiveMQ + Artemis can be configured to batch up writes for a maximum of `batchDelay` + milliseconds. This can increase overall throughput for very small messages. It + does so at the expense of an increase in average latency for message transfer. + The default value for this property is `0` ms. + +- `directDeliver`. When a message arrives on the server and is delivered to + waiting consumers, by default, the delivery is done on the same thread as + that on which the message arrived. This gives good latency in environments with + relatively small messages and a small number of consumers, but at the cost of + overall throughput and scalability - especially on multi-core machines. If you + want the lowest latency and a possible reduction in throughput then you can use + the default value for `directDeliver` (i.e. `true`). If you are willing to take + some small extra hit on latency but want the highest throughput set + `directDeliver` to `false`. + +- `nioRemotingThreads` This is deprecated. It is replaced by `remotingThreads`, + if you are using this please update your configuration + +- `remotingThreads`. Apache ActiveMQ Artemis will, by default, use a number of + threads equal to three times the number of cores (or hyper-threads) as + reported by `Runtime.getRuntime().availableProcessors()` for processing + incoming packets. If you want to override this value, you can set the number of + threads by specifying this parameter. The default value for this parameter is + `-1` which means use the value from + `Runtime.getRuntime().availableProcessors()` \* 3. + +- `localAddress`. When configured a Netty Connector it is possible to specify + which local address the client will use when connecting to the remote + address. This is typically used in the Application Server or when running + Embedded to control which address is used for outbound connections. If the + local-address is not set then the connector will use any local address + available + +- `localPort`. When configured a Netty Connector it is possible to specify + which local port the client will use when connecting to the remote address. + This is typically used in the Application Server or when running Embedded to + control which port is used for outbound connections. If the local-port default + is used, which is 0, then the connector will let the system pick up an + ephemeral port. valid ports are 0 to 65535 + +- `connectionsAllowed`. This is only valid for acceptors. It limits the number + of connections which the acceptor will allow. When this limit is reached a + DEBUG level message is issued to the log, and the connection is refused. The + type of client in use will determine what happens when the connection is + refused. In the case of a `core` client, it will result in a + `org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException`. + +- `handshake-timeout`. Prevents an unauthorised client opening a large number + of connections and just keeping them open. As connections each require a file + handle this consumes resources that are then unavailable to other clients. Once + the connection is authenticated, the usual rules can be enforced regarding + resource consumption. Default value is set to 10 seconds. Each integer is valid + value. When set value to zero or negative integer this feature is turned off. + Changing value needs to restart server to take effect. ### Configuring Netty Native Transport -Netty Native Transport support exists for selected OS platforms. -This allows Apache ActiveMQ Artemis to use native sockets/io instead of Java NIO. +Netty Native Transport support exists for selected OS platforms. This allows +Apache ActiveMQ Artemis to use native sockets/io instead of Java NIO. -These Native transports add features specific to a particular platform, -generate less garbage, and generally improve performance when compared to Java NIO based transport. +These Native transports add features specific to a particular platform, +generate less garbage, and generally improve performance when compared to Java +NIO based transport. Both Clients and Server can benefit from this. Current Supported Platforms. -- Linux running 64bit JVM -- MacOS running 64bit JVM +- Linux running 64bit JVM +- MacOS running 64bit JVM -Apache ActiveMQ Artemis will by default enable the corresponding native transport if a supported platform is detected. +Apache ActiveMQ Artemis will by default enable the corresponding native +transport if a supported platform is detected. -If running on an unsupported platform or any issues loading native libs, Apache ActiveMQ Artemis will fallback onto Java NIO. +If running on an unsupported platform or any issues loading native libs, Apache +ActiveMQ Artemis will fallback onto Java NIO. #### Linux Native Transport -On supported Linux platforms Epoll is used, @see https://en.wikipedia.org/wiki/Epoll. +On supported Linux platforms Epoll is used, @see +https://en.wikipedia.org/wiki/Epoll. The following properties are specific to this native transport: -- `useEpoll` enables the use of epoll if a supported linux platform is running a 64bit JVM is detected. - Setting this to `false` will force the use of Java NIO instead of epoll. Default is `true` +- `useEpoll` enables the use of epoll if a supported linux platform is running + a 64bit JVM is detected. Setting this to `false` will force the use of Java + NIO instead of epoll. Default is `true` #### MacOS Native Transport -On supported MacOS platforms KQueue is used, @see https://en.wikipedia.org/wiki/Kqueue. +On supported MacOS platforms KQueue is used, @see +https://en.wikipedia.org/wiki/Kqueue. The following properties are specific to this native transport: -- `useKQueue` enables the use of kqueue if a supported MacOS platform running a 64bit JVM is detected. - Setting this to `false` will force the use of Java NIO instead of kqueue. Default is `true` +- `useKQueue` enables the use of kqueue if a supported MacOS platform running a + 64bit JVM is detected. Setting this to `false` will force the use of Java + NIO instead of kqueue. Default is `true` - ### Configuring Netty SSL -Netty SSL is similar to the Netty TCP transport but it provides -additional security by encrypting TCP connections using the Secure -Sockets Layer SSL +Netty SSL is similar to the Netty TCP transport but it provides additional +security by encrypting TCP connections using the Secure Sockets Layer SSL Please see the examples for a full working example of using Netty SSL. -Netty SSL uses all the same properties as Netty TCP but adds the -following additional properties: - -- `sslEnabled` - - Must be `true` to enable SSL. Default is `false`. - -- `keyStorePath` - - When used on an `acceptor` this is the path to the SSL key store on - the server which holds the server's certificates (whether - self-signed or signed by an authority). - - When used on a `connector` this is the path to the client-side SSL - key store which holds the client certificates. This is only relevant - for a `connector` if you are using 2-way SSL (i.e. mutual - authentication). Although this value is configured on the server, it - is downloaded and used by the client. If the client needs to use a - different path from that set on the server then it can override the - server-side setting by either using the customary - "javax.net.ssl.keyStore" system property or the ActiveMQ-specific - "org.apache.activemq.ssl.keyStore" system property. The - ActiveMQ-specific system property is useful if another component on - client is already making use of the standard, Java system property. - -- `keyStorePassword` - - When used on an `acceptor` this is the password for the server-side - keystore. - - When used on a `connector` this is the password for the client-side - keystore. This is only relevant for a `connector` if you are using - 2-way SSL (i.e. mutual authentication). Although this value can be - configured on the server, it is downloaded and used by the client. - If the client needs to use a different password from that set on the - server then it can override the server-side setting by either using - the customary "javax.net.ssl.keyStorePassword" system property or - the ActiveMQ-specific "org.apache.activemq.ssl.keyStorePassword" - system property. The ActiveMQ-specific system property is useful if - another component on client is already making use of the standard, - Java system property. - -- `trustStorePath` - - When used on an `acceptor` this is the path to the server-side SSL - key store that holds the keys of all the clients that the server - trusts. This is only relevant for an `acceptor` if you are using - 2-way SSL (i.e. mutual authentication). - - When used on a `connector` this is the path to the client-side SSL - key store which holds the public keys of all the servers that the - client trusts. Although this value can be configured on the server, - it is downloaded and used by the client. If the client needs to use - a different path from that set on the server then it can override - the server-side setting by either using the customary - "javax.net.ssl.trustStore" system property or the ActiveMQ-specific - "org.apache.activemq.ssl.trustStore" system property. The - ActiveMQ-specific system property is useful if another component on - client is already making use of the standard, Java system property. - -- `trustStorePassword` - - When used on an `acceptor` this is the password for the server-side - trust store. This is only relevant for an `acceptor` if you are - using 2-way SSL (i.e. mutual authentication). - - When used on a `connector` this is the password for the client-side - truststore. Although this value can be configured on the server, it - is downloaded and used by the client. If the client needs to use a - different password from that set on the server then it can override - the server-side setting by either using the customary - "javax.net.ssl.trustStorePassword" system property or the - ActiveMQ-specific "org.apache.activemq.ssl.trustStorePassword" - system property. The ActiveMQ-specific system property is useful if - another component on client is already making use of the standard, - Java system property. - -- `enabledCipherSuites` - - Whether used on an `acceptor` or `connector` this is a comma - separated list of cipher suites used for SSL communication. The - default value is `null` which means the JVM's default will be used. - -- `enabledProtocols` - - Whether used on an `acceptor` or `connector` this is a comma - separated list of protocols used for SSL communication. The default - value is `null` which means the JVM's default will be used. - -- `needClientAuth` - - This property is only for an `acceptor`. It tells a client - connecting to this acceptor that 2-way SSL is required. Valid values - are `true` or `false`. Default is `false`. +Netty SSL uses all the same properties as Netty TCP but adds the following +additional properties: + +- `sslEnabled` + + Must be `true` to enable SSL. Default is `false`. + +- `keyStorePath` + + When used on an `acceptor` this is the path to the SSL key store on the + server which holds the server's certificates (whether self-signed or signed by + an authority). + + When used on a `connector` this is the path to the client-side SSL key store + which holds the client certificates. This is only relevant for a `connector` if + you are using 2-way SSL (i.e. mutual authentication). Although this value is + configured on the server, it is downloaded and used by the client. If the + client needs to use a different path from that set on the server then it can + override the server-side setting by either using the customary + "javax.net.ssl.keyStore" system property or the ActiveMQ-specific + "org.apache.activemq.ssl.keyStore" system property. The ActiveMQ-specific + system property is useful if another component on client is already making use + of the standard, Java system property. + +- `keyStorePassword` + + When used on an `acceptor` this is the password for the server-side keystore. + + When used on a `connector` this is the password for the client-side keystore. + This is only relevant for a `connector` if you are using 2-way SSL (i.e. mutual + authentication). Although this value can be configured on the server, it is + downloaded and used by the client. If the client needs to use a different + password from that set on the server then it can override the server-side + setting by either using the customary "javax.net.ssl.keyStorePassword" system + property or the ActiveMQ-specific "org.apache.activemq.ssl.keyStorePassword" + system property. The ActiveMQ-specific system property is useful if another + component on client is already making use of the standard, Java system + property. + +- `trustStorePath` + + When used on an `acceptor` this is the path to the server-side SSL key store + that holds the keys of all the clients that the server trusts. This is only + relevant for an `acceptor` if you are using 2-way SSL (i.e. mutual + authentication). + + When used on a `connector` this is the path to the client-side SSL key store + which holds the public keys of all the servers that the client trusts. Although + this value can be configured on the server, it is downloaded and used by the + client. If the client needs to use a different path from that set on the server + then it can override the server-side setting by either using the customary + "javax.net.ssl.trustStore" system property or the ActiveMQ-specific + "org.apache.activemq.ssl.trustStore" system property. The ActiveMQ-specific + system property is useful if another component on client is already making use + of the standard, Java system property. + +- `trustStorePassword` + + When used on an `acceptor` this is the password for the server-side trust + store. This is only relevant for an `acceptor` if you are using 2-way SSL (i.e. + mutual authentication). + + When used on a `connector` this is the password for the client-side + truststore. Although this value can be configured on the server, it is + downloaded and used by the client. If the client needs to use a different + password from that set on the server then it can override the server-side + setting by either using the customary "javax.net.ssl.trustStorePassword" system + property or the ActiveMQ-specific "org.apache.activemq.ssl.trustStorePassword" + system property. The ActiveMQ-specific system property is useful if another + component on client is already making use of the standard, Java system + property. + +- `enabledCipherSuites` + + Whether used on an `acceptor` or `connector` this is a comma separated list + of cipher suites used for SSL communication. The default value is `null` which + means the JVM's default will be used. + +- `enabledProtocols` + + Whether used on an `acceptor` or `connector` this is a comma separated list + of protocols used for SSL communication. The default value is `null` which + means the JVM's default will be used. + +- `needClientAuth` + + This property is only for an `acceptor`. It tells a client connecting to this + acceptor that 2-way SSL is required. Valid values are `true` or `false`. + Default is `false`. - Note that this property takes precedence over `wantClientAuth` and if - its value is set to true then `wantClientAuth` will be ignored. + **Note:** This property takes precedence over `wantClientAuth` and if its + value is set to true then `wantClientAuth` will be ignored. -- `wantClientAuth` +- `wantClientAuth` - This property is only for an `acceptor`. It tells a client - connecting to this acceptor that 2-way SSL is requested but not required. - Valid values are `true` or `false`. Default is `false`. + This property is only for an `acceptor`. It tells a client connecting to this + acceptor that 2-way SSL is requested but not required. Valid values are `true` + or `false`. Default is `false`. - Note that if the property `needClientAuth` is set to true then that - property will take precedence and this property will be ignored. + **Note:** If the property `needClientAuth` is set to `true` then that + property will take precedence and this property will be ignored. -- `verifyHost` +- `verifyHost` - When used on an `acceptor` the `CN` of the connecting client's SSL certificate - will be compared to its hostname to verify they match. This is useful - only for 2-way SSL. + When used on an `acceptor` the `CN` of the connecting client's SSL + certificate will be compared to its hostname to verify they match. This is + useful only for 2-way SSL. - When used on a `connector` the `CN` of the server's SSL certificate will be - compared to its hostname to verify they match. This is useful for both 1-way - and 2-way SSL. + When used on a `connector` the `CN` of the server's SSL certificate will be + compared to its hostname to verify they match. This is useful for both 1-way + and 2-way SSL. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `trustAll` +- `trustAll` - When used on a `connector` the client will trust the provided server certificate - implicitly, regardless of any configured trust store. **Warning:** This setting is - primarily for testing purposes only and should not be used in production. + When used on a `connector` the client will trust the provided server + certificate implicitly, regardless of any configured trust store. **Warning:** + This setting is primarily for testing purposes only and should not be used in + production. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `forceSSLParameters` +- `forceSSLParameters` - When used on a `connector` any SSL settings that are set as parameters on the connector will - be used instead of JVM system properties including both javax.net.ssl and ActiveMQ system properties - to configure the SSL context for this connector. + When used on a `connector` any SSL settings that are set as parameters on the + connector will be used instead of JVM system properties including both + javax.net.ssl and ActiveMQ system properties to configure the SSL context for + this connector. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `useDefaultSslContext` +- `useDefaultSslContext` - Only valid on a `connector`. Allows the `connector` to use the "default" SSL - context (via `SSLContext.getDefault()`) which can be set programmatically by - the client (via `SSLContext.setDefault(SSLContext)`). If set to `true` all - other SSL related parameters except for `sslEnabled` are ignored. + Only valid on a `connector`. Allows the `connector` to use the "default" SSL + context (via `SSLContext.getDefault()`) which can be set programmatically by + the client (via `SSLContext.setDefault(SSLContext)`). If set to `true` all + other SSL related parameters except for `sslEnabled` are ignored. - Valid values are `true` or `false`. Default is `false`. + Valid values are `true` or `false`. Default is `false`. -- `sslProvider` - - Used to change the SSL Provider between `JDK` and `OPENSSL`. The default is `JDK`. - If used with `OPENSSL` you can add `netty-tcnative` to your classpath to use the native - installed openssl. This can be useful if you want to use special ciphersuite - elliptic curve combinations - which are support through openssl but not through the JDK provider. See https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations - for more information's. - - +- `sslProvider` + Used to change the SSL Provider between `JDK` and `OPENSSL`. The default is + `JDK`. If used with `OPENSSL` you can add `netty-tcnative` to your classpath + to use the native installed openssl. This can be useful if you want to use + special ciphersuite - elliptic curve combinations which are support through + openssl but not through the JDK provider. See + https://en.wikipedia.org/wiki/Comparison_of_TLS_implementations for more + information's. + ### Configuring Netty HTTP Netty HTTP tunnels packets over the HTTP protocol. It can be useful in @@ -481,22 +470,22 @@ Please see the examples for a full working example of using Netty HTTP. Netty HTTP uses the same properties as Netty TCP but adds the following additional properties: -- `httpEnabled`. This is now no longer needed. With single port support - Apache ActiveMQ Artemis will now automatically detect if http is being - used and configure itself. +- `httpEnabled`. This is now no longer needed. With single port support Apache + ActiveMQ Artemis will now automatically detect if http is being used and + configure itself. -- `httpClientIdleTime`. How long a client can be idle before - sending an empty http request to keep the connection alive +- `httpClientIdleTime`. How long a client can be idle before sending an empty + http request to keep the connection alive -- `httpClientIdleScanPeriod`. How often, in milliseconds, to scan - for idle clients +- `httpClientIdleScanPeriod`. How often, in milliseconds, to scan for idle + clients -- `httpResponseTime`. How long the server can wait before sending an - empty http response to keep the connection alive +- `httpResponseTime`. How long the server can wait before sending an empty http + response to keep the connection alive -- `httpServerScanPeriod`. How often, in milliseconds, to scan for - clients needing responses +- `httpServerScanPeriod`. How often, in milliseconds, to scan for clients + needing responses -- `httpRequiresSessionId`. If `true` the client will wait after the - first call to receive a session id. Used the http connector is - connecting to servlet acceptor (not recommended) +- `httpRequiresSessionId`. If `true` the client will wait after the first call + to receive a session id. Used the http connector is connecting to servlet + acceptor (not recommended) diff --git a/docs/user-manual/en/connection-ttl.md b/docs/user-manual/en/connection-ttl.md index a1876bd0961..3677d6fdca0 100644 --- a/docs/user-manual/en/connection-ttl.md +++ b/docs/user-manual/en/connection-ttl.md @@ -18,8 +18,7 @@ ServerLocator locator = null; ClientSessionFactory sf = null; ClientSession session = null; -try -{ +try { locator = ActiveMQClient.createServerLocatorWithoutHA(..); sf = locator.createClientSessionFactory();; @@ -27,21 +26,16 @@ try session = sf.createSession(...); ... do some stuff with the session... -} -finally -{ - if (session != null) - { +} finally { + if (session != null) { session.close(); } - if (sf != null) - { + if (sf != null) { sf.close(); } - if(locator != null) - { + if(locator != null) { locator.close(); } } @@ -52,18 +46,14 @@ And here's an example of a well behaved JMS client application: ```java Connection jmsConnection = null; -try -{ +try { ConnectionFactory jmsConnectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); jmsConnection = jmsConnectionFactory.createConnection(); ... do some stuff with the connection... -} -finally -{ - if (connection != null) - { +} finally { + if (connection != null) { connection.close(); } } @@ -73,17 +63,13 @@ finally Or with using auto-closeable feature from Java, which can save a few lines of code: ```java - - try ( ActiveMQConnectionFactory jmsConnectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); - Connection jmsConnection = jmsConnectionFactory.createConnection()) -{ + Connection jmsConnection = jmsConnectionFactory.createConnection()) { ... do some stuff with the connection... } ``` - Unfortunately users don't always write well behaved applications, and sometimes clients just crash so they don't have a chance to clean up their resources! @@ -186,17 +172,17 @@ from a thread pool so that the remoting thread is not tied up for too long. Please note that processing operations asynchronously on another thread adds a little more latency. These packets are: -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.RollbackMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.RollbackMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCloseMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCloseMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCommitMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionCommitMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXACommitMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXACommitMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAPrepareMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXAPrepareMessage` -- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXARollbackMessage` +- `org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionXARollbackMessage` To disable asynchronous connection execution, set the parameter `async-connection-execution-enabled` in `broker.xml` to diff --git a/docs/user-manual/en/core-bridges.md b/docs/user-manual/en/core-bridges.md index 668a3b5a4a9..ede12abefae 100644 --- a/docs/user-manual/en/core-bridges.md +++ b/docs/user-manual/en/core-bridges.md @@ -1,45 +1,46 @@ # Core Bridges The function of a bridge is to consume messages from a source queue, and -forward them to a target address, typically on a different Apache ActiveMQ Artemis -server. +forward them to a target address, typically on a different Apache ActiveMQ +Artemis server. -The source and target servers do not have to be in the same cluster -which makes bridging suitable for reliably sending messages from one -cluster to another, for instance across a WAN, or internet and where the -connection may be unreliable. +The source and target servers do not have to be in the same cluster which makes +bridging suitable for reliably sending messages from one cluster to another, +for instance across a WAN, or internet and where the connection may be +unreliable. The bridge has built in resilience to failure so if the target server connection is lost, e.g. due to network failure, the bridge will retry -connecting to the target until it comes back online. When it comes back -online it will resume operation as normal. +connecting to the target until it comes back online. When it comes back online +it will resume operation as normal. -In summary, bridges are a way to reliably connect two separate Apache ActiveMQ Artemis -servers together. With a core bridge both source and target servers must -be Apache ActiveMQ Artemis servers. +In summary, bridges are a way to reliably connect two separate Apache ActiveMQ +Artemis servers together. With a core bridge both source and target servers +must be Apache ActiveMQ Artemis servers. -Bridges can be configured to provide *once and only once* delivery -guarantees even in the event of the failure of the source or the target -server. They do this by using duplicate detection (described in [Duplicate Detection](duplicate-detection.md)). +Bridges can be configured to provide *once and only once* delivery guarantees +even in the event of the failure of the source or the target server. They do +this by using duplicate detection (described in [Duplicate +Detection](duplicate-detection.md)). -> **Note** +> **Note:** > -> Although they have similar function, don't confuse core bridges with -> JMS bridges! +> Although they have similar function, don't confuse core bridges with JMS +> bridges! > -> Core bridges are for linking an Apache ActiveMQ Artemis node with another Apache ActiveMQ Artemis -> node and do not use the JMS API. A JMS Bridge is used for linking any -> two JMS 1.1 compliant JMS providers. So, a JMS Bridge could be used -> for bridging to or from different JMS compliant messaging system. It's -> always preferable to use a core bridge if you can. Core bridges use -> duplicate detection to provide *once and only once* guarantees. To -> provide the same guarantee using a JMS bridge you would have to use XA -> which has a higher overhead and is more complex to configure. +> Core bridges are for linking an Apache ActiveMQ Artemis node with another +> Apache ActiveMQ Artemis node and do not use the JMS API. A JMS Bridge is used +> for linking any two JMS 1.1 compliant JMS providers. So, a JMS Bridge could +> be used for bridging to or from different JMS compliant messaging system. +> It's always preferable to use a core bridge if you can. Core bridges use +> duplicate detection to provide *once and only once* guarantees. To provide +> the same guarantee using a JMS bridge you would have to use XA which has a +> higher overhead and is more complex to configure. ## Configuring Bridges -Bridges are configured in `broker.xml`. Let's kick off -with an example (this is actually from the bridge example): +Bridges are configured in `broker.xml`. Let's kick off with an example (this is +actually from the bridge example): ```xml @@ -68,154 +69,144 @@ with an example (this is actually from the bridge example): ``` -In the above example we have shown all the parameters its possible to -configure for a bridge. In practice you might use many of the defaults -so it won't be necessary to specify them all explicitly. +In the above example we have shown all the parameters its possible to configure +for a bridge. In practice you might use many of the defaults so it won't be +necessary to specify them all explicitly. Let's take a look at all the parameters in turn: -- `name` attribute. All bridges must have a unique name in the server. +- `name` attribute. All bridges must have a unique name in the server. -- `queue-name`. This is the unique name of the local queue that the - bridge consumes from, it's a mandatory parameter. +- `queue-name`. This is the unique name of the local queue that the bridge + consumes from, it's a mandatory parameter. - The queue must already exist by the time the bridge is instantiated - at start-up. - -- `forwarding-address`. This is the address on the target server that - the message will be forwarded to. If a forwarding address is not - specified, then the original address of the message will be - retained. - -- `filter-string`. An optional filter string can be supplied. If - specified then only messages which match the filter expression - specified in the filter string will be forwarded. The filter string - follows the ActiveMQ Artemis filter expression syntax described in [Filter Expressions](filter-expressions.md). - -- `transformer-class-name`. An optional transformer-class-name can be - specified. This is the name of a user-defined class which implements - the `org.apache.activemq.artemis.core.server.transformer.Transformer` interface. - - If this is specified then the transformer's `transform()` method - will be invoked with the message before it is forwarded. This gives - you the opportunity to transform the message's header or body before - forwarding it. - -- `ha`. This optional parameter determines whether or not this bridge - should support high availability. True means it will connect to any - available server in a cluster and support failover. The default - value is `false`. - -- `retry-interval`. This optional parameter determines the period in - milliseconds between subsequent reconnection attempts, if the - connection to the target server has failed. The default value is - `2000`milliseconds. - -- `retry-interval-multiplier`. This optional parameter determines - determines a multiplier to apply to the time since the last retry to - compute the time to the next retry. - - This allows you to implement an *exponential backoff* between retry - attempts. - - Let's take an example: - - If we set `retry-interval`to `1000` ms and we set - `retry-interval-multiplier` to `2.0`, then, if the first reconnect - attempt fails, we will wait `1000` ms then `2000` ms then `4000` ms - between subsequent reconnection attempts. - - The default value is `1.0` meaning each reconnect attempt is spaced - at equal intervals. - -- `initial-connect-attempts`. This optional parameter determines the - total number of initial connect attempts the bridge will make before - giving up and shutting down. A value of `-1` signifies an unlimited - number of attempts. The default value is `-1`. - -- `reconnect-attempts`. This optional parameter determines the total - number of reconnect attempts the bridge will make before giving up - and shutting down. A value of `-1` signifies an unlimited number of - attempts. The default value is `-1`. - -- `failover-on-server-shutdown`. This optional parameter determines - whether the bridge will attempt to failover onto a backup server (if - specified) when the target server is cleanly shutdown rather than - crashed. - - The bridge connector can specify both a live and a backup server, if - it specifies a backup server and this parameter is set to `true` - then if the target server is *cleanly* shutdown the bridge - connection will attempt to failover onto its backup. If the bridge - connector has no backup server configured then this parameter has no - effect. - - Sometimes you want a bridge configured with a live and a backup - target server, but you don't want to failover to the backup if the - live server is simply taken down temporarily for maintenance, this - is when this parameter comes in handy. - - The default value for this parameter is `false`. - -- `use-duplicate-detection`. This optional parameter determines - whether the bridge will automatically insert a duplicate id property - into each message that it forwards. - - Doing so, allows the target server to perform duplicate detection on - messages it receives from the source server. If the connection fails - or server crashes, then, when the bridge resumes it will resend - unacknowledged messages. This might result in duplicate messages - being sent to the target server. By enabling duplicate detection - allows these duplicates to be screened out and ignored. - - This allows the bridge to provide a *once and only once* delivery - guarantee without using heavyweight methods such as XA (see [Duplicate Detection](duplicate-detection.md) for - more information). - - The default value for this parameter is `true`. - -- `confirmation-window-size`. This optional parameter determines the - `confirmation-window-size` to use for the connection used to forward - messages to the target node. This attribute is described in section - [Reconnection and Session Reattachment](client-reconnection.md) - - > **Warning** - > - > When using the bridge to forward messages to an address which uses - > the `BLOCK` `address-full-policy` from a queue which has a - > `max-size-bytes` set it's important that - > `confirmation-window-size` is less than or equal to - > `max-size-bytes` to prevent the flow of messages from ceasing. - -- `producer-window-size`. This optional parameter determines the - producer flow control through the bridge. You usually leave this off - unless you are dealing with huge large messages. + The queue must already exist by the time the bridge is instantiated at + start-up. + +- `forwarding-address`. This is the address on the target server that the + message will be forwarded to. If a forwarding address is not specified, then + the original address of the message will be retained. + +- `filter-string`. An optional filter string can be supplied. If specified then + only messages which match the filter expression specified in the filter + string will be forwarded. The filter string follows the ActiveMQ Artemis filter + expression syntax described in [Filter Expressions](filter-expressions.md). + +- `transformer-class-name`. An optional transformer-class-name can be + specified. This is the name of a user-defined class which implements the + `org.apache.activemq.artemis.core.server.transformer.Transformer` interface. + + If this is specified then the transformer's `transform()` method will be + invoked with the message before it is forwarded. This gives you the opportunity + to transform the message's header or body before forwarding it. + +- `ha`. This optional parameter determines whether or not this bridge should + support high availability. True means it will connect to any available server + in a cluster and support failover. The default value is `false`. + +- `retry-interval`. This optional parameter determines the period in + milliseconds between subsequent reconnection attempts, if the connection to + the target server has failed. The default value is `2000`milliseconds. + +- `retry-interval-multiplier`. This optional parameter determines determines a + multiplier to apply to the time since the last retry to compute the time to + the next retry. + + This allows you to implement an *exponential backoff* between retry + attempts. + + Let's take an example: + + If we set `retry-interval`to `1000` ms and we set `retry-interval-multiplier` + to `2.0`, then, if the first reconnect attempt fails, we will wait `1000` ms + then `2000` ms then `4000` ms between subsequent reconnection attempts. + + The default value is `1.0` meaning each reconnect attempt is spaced at equal + intervals. + +- `initial-connect-attempts`. This optional parameter determines the total + number of initial connect attempts the bridge will make before giving up and + shutting down. A value of `-1` signifies an unlimited number of attempts. The + default value is `-1`. + +- `reconnect-attempts`. This optional parameter determines the total number of + reconnect attempts the bridge will make before giving up and shutting down. A + value of `-1` signifies an unlimited number of attempts. The default value is + `-1`. + +- `failover-on-server-shutdown`. This optional parameter determines whether the + bridge will attempt to failover onto a backup server (if specified) when the + target server is cleanly shutdown rather than crashed. + + The bridge connector can specify both a live and a backup server, if it + specifies a backup server and this parameter is set to `true` then if the + target server is *cleanly* shutdown the bridge connection will attempt to + failover onto its backup. If the bridge connector has no backup server + configured then this parameter has no effect. + + Sometimes you want a bridge configured with a live and a backup target + server, but you don't want to failover to the backup if the live server is + simply taken down temporarily for maintenance, this is when this parameter + comes in handy. + + The default value for this parameter is `false`. + +- `use-duplicate-detection`. This optional parameter determines whether the + bridge will automatically insert a duplicate id property into each message + that it forwards. + + Doing so, allows the target server to perform duplicate detection on messages + it receives from the source server. If the connection fails or server crashes, + then, when the bridge resumes it will resend unacknowledged messages. This + might result in duplicate messages being sent to the target server. By enabling + duplicate detection allows these duplicates to be screened out and ignored. + + This allows the bridge to provide a *once and only once* delivery guarantee + without using heavyweight methods such as XA (see [Duplicate + Detection](duplicate-detection.md) for more information). + + The default value for this parameter is `true`. + +- `confirmation-window-size`. This optional parameter determines the + `confirmation-window-size` to use for the connection used to forward messages + to the target node. This attribute is described in section [Reconnection and + Session Reattachment](client-reconnection.md) + + > **Warning** + > + > When using the bridge to forward messages to an address which uses the + > `BLOCK` `address-full-policy` from a queue which has a `max-size-bytes` set + > it's important that `confirmation-window-size` is less than or equal to + > `max-size-bytes` to prevent the flow of messages from ceasing. + +- `producer-window-size`. This optional parameter determines the producer flow + control through the bridge. You usually leave this off unless you are dealing + with huge large messages. - Default=-1 (disabled) - -- `user`. This optional parameter determines the user name to use when - creating the bridge connection to the remote server. If it is not - specified the default cluster user specified by `cluster-user` in - `broker.xml` will be used. - -- `password`. This optional parameter determines the password to use - when creating the bridge connection to the remote server. If it is - not specified the default cluster password specified by - `cluster-password` in `broker.xml` will be used. - -- `static-connectors` or `discovery-group-ref`. Pick either of these - options to connect the bridge to the target server. - - The `static-connectors` is a list of `connector-ref` elements - pointing to `connector` elements defined elsewhere. A *connector* - encapsulates knowledge of what transport to use (TCP, SSL, HTTP etc) - as well as the server connection parameters (host, port etc). For - more information about what connectors are and how to configure - them, please see [Configuring the Transport](configuring-transports.md). - - The `discovery-group-ref` element has one attribute - - `discovery-group-name`. This attribute points to a `discovery-group` - defined elsewhere. For more information about what discovery-groups - are and how to configure them, please see [Discovery Groups](clusters.md). + Default=-1 (disabled) + +- `user`. This optional parameter determines the user name to use when creating + the bridge connection to the remote server. If it is not specified the + default cluster user specified by `cluster-user` in `broker.xml` will be used. + +- `password`. This optional parameter determines the password to use when + creating the bridge connection to the remote server. If it is not specified + the default cluster password specified by `cluster-password` in `broker.xml` + will be used. + +- `static-connectors` or `discovery-group-ref`. Pick either of these options to + connect the bridge to the target server. + + The `static-connectors` is a list of `connector-ref` elements pointing to + `connector` elements defined elsewhere. A *connector* encapsulates knowledge of + what transport to use (TCP, SSL, HTTP etc) as well as the server connection + parameters (host, port etc). For more information about what connectors are and + how to configure them, please see [Configuring the + Transport](configuring-transports.md). + + The `discovery-group-ref` element has one attribute - `discovery-group-name`. + This attribute points to a `discovery-group` defined elsewhere. For more + information about what discovery-groups are and how to configure them, please + see [Discovery Groups](clusters.md). diff --git a/docs/user-manual/en/core.md b/docs/user-manual/en/core.md new file mode 100644 index 00000000000..cd8d131a549 --- /dev/null +++ b/docs/user-manual/en/core.md @@ -0,0 +1,228 @@ +# Using Core + +Apache ActiveMQ Artemis core is a messaging system with its own API. We call +this the *core API*. + +If you don't want to use the JMS API or any of the other supported protocols +you can use the core API directly. The core API provides all the functionality +of JMS but without much of the complexity. It also provides features that are +not available using JMS. + +## Core Messaging Concepts + +Some of the core messaging concepts are similar to JMS concepts, but core +messaging concepts are also different in some ways as well. In general the core +API is simpler than the JMS API, since we remove distinctions between queues, +topics and subscriptions. We'll discuss each of the major core messaging +concepts in turn, but to see the API in detail please consult the Javadoc. + +Also refer to the [addressing model](address-model.md) chapter for a high-level +overview of these concepts as well as configuration details. + +### Message + +- A message is the unit of data which is sent between clients and servers. + +- A message has a body which is a buffer containing convenient methods for + reading and writing data into it. + +- A message has a set of properties which are key-value pairs. Each property + key is a string and property values can be of type integer, long, short, + byte, byte[], String, double, float or boolean. + +- A message has an *address* it is being sent to. When the message arrives on + the server it is routed to any queues that are bound to the address. The + routing semantics (i.e. anycast or multicast) are determined by the "routing + type" of the address and queue. If the queues are bound with any filter, the + message will only be routed to that queue if the filter matches. An address may + have many queues bound to it or even none. There may also be entities other + than queues (e.g. *diverts*) bound to addresses. + +- Messages can be either durable or non durable. Durable messages in a durable + queue will survive a server crash or restart. Non durable messages will never + survive a server crash or restart. + +- Messages can be specified with a priority value between 0 and 9. 0 represents + the lowest priority and 9 represents the highest. The broker will attempt to + deliver higher priority messages before lower priority ones. + +- Messages can be specified with an optional expiry time. The broker will not + deliver messages after its expiry time has been exceeded. + +- Messages also have an optional timestamp which represents the time the + message was sent. + +- Apache ActiveMQ Artemis also supports the sending/consuming of very large + messages much larger than can fit in available RAM at any one time. + +### Address + +A server maintains a mapping between an address and a set of queues. Zero or +more queues can be bound to a single address. Each queue can be bound with an +optional message filter. When a message is routed, it is routed to the set of +queues bound to the message's address. If any of the queues are bound with a +filter expression, then the message will only be routed to the subset of bound +queues which match that filter expression. + +Other entities, such as *diverts* can also be bound to an address and messages +will also be routed there. + +> **Note:** +> +> Although core supports publish-subscribe semantics there is no such thing as +> a "topic" per se. "Topic" is mainly a JMS term. In core we just deal with +> *addresses*, *queues*, and *routing types*. +> +> For example, a JMS topic would be implemented by a single address to which +> many queues are bound using multicast routing. Each queue represents a +> "subscription" in normal "topic" terms. A JMS queue would be implemented as a +> single address to which one queue is bound using anycast routing - that queue +> represents the JMS queue. + +### Queue + +Queues can be durable, meaning the messages they contain survive a server crash +or restart, as long as the messages in them are durable. Non durable queues do +not survive a server restart or crash even if the messages they contain are +durable. + +Queues can also be temporary, meaning they are automatically deleted when the +client connection is closed, if they are not explicitly deleted before that. + +Queues can be bound with an optional filter expression. If a filter expression +is supplied then the server will only route messages that match that filter +expression to any queues bound to the address. + +Many queues can be bound to a single address. A particular queue is only bound +to a maximum of one address. + +### Routing Type + +The routing type determines the semantics used when routing messages to the +queues bound to the address where the message was sent. Two types are +supported: + +- `ANYCAST` + + The message is routed to only **one** of the queues bound to the address. If + multiple queues are bound to the address then messages are routed to them in a + round-robin fashion. + +- `MULTICAST` + + The message is route to **all** of the queues bound to the address. + +## Core API + +### ServerLocator + +Clients use `ServerLocator` instances to create `ClientSessionFactory` +instances. `ServerLocator` instances are used to locate servers and create +connections to them. + +In JMS terms think of a `ServerLocator` in the same way you would a JMS +Connection Factory. + +`ServerLocator` instances are created using the `ActiveMQClient` factory class. + +### ClientSessionFactory + +Clients use `ClientSessionFactory` instances to create `ClientSession` +instances. `ClientSessionFactory` instances are basically the connection to a +server + +In JMS terms think of them as JMS Connections. + +`ClientSessionFactory` instances are created using the `ServerLocator` class. + +### ClientSession + +A client uses a `ClientSession`for consuming and producing messages and for +grouping them in transactions. `ClientSession` instances can support both +transactional and non transactional semantics and also provide an `XAResource` +interface so messaging operations can be performed as part of a +[JTA](http://www.oracle.com/technetwork/java/javaee/tech/jta-138684.html) +transaction. + +`ClientSession` instances group `ClientConsumer` instances and `ClientProducer` +instances. + +`ClientSession` instances can be registered with an optional +`SendAcknowledgementHandler`. This allows your client code to be notified +asynchronously when sent messages have successfully reached the server. This +unique Apache ActiveMQ Artemis feature, allows you to have full guarantees that +sent messages have reached the server without having to block on each message +sent until a response is received. Blocking on each messages sent is costly +since it requires a network round trip for each message sent. By not blocking +and receiving send acknowledgements asynchronously you can create true end to +end asynchronous systems which is not possible using the standard JMS API. For +more information on this advanced feature please see the section [Guarantees of +sends and commits](send-guarantees.md). + +### ClientConsumer + +Clients use `ClientConsumer` instances to consume messages from a queue. Core +messaging supports both synchronous and asynchronous message consumption +semantics. `ClientConsumer` instances can be configured with an optional filter +expression and will only consume messages which match that expression. + +### ClientProducer + +Clients create `ClientProducer` instances on `ClientSession` instances so they +can send messages. `ClientProducer` instances can specify an address to which +all sent messages are routed, or they can have no specified address, and the +address is specified at send time for the message. + +> **Warning** +> +> Please note that `ClientSession`, `ClientProducer` and `ClientConsumer` +> instances are *designed to be re-used*. +> +> It's an anti-pattern to create new `ClientSession`, `ClientProducer` and +> `ClientConsumer` instances for each message you produce or consume. If you do +> this, your application will perform very poorly. This is discussed further +> in the section on performance tuning [Performance Tuning](perf-tuning.md). + +## A simple example of using Core + +Here's a very simple program using the core messaging API to send and receive a +message. Logically it's comprised of two sections: firstly setting up the +producer to write a message to an *addresss*, and secondly, creating a *queue* +for the consumer using anycast routing, creating the consumer, and *starting* +it. + +```java +ServerLocator locator = ActiveMQClient.createServerLocator("vm://0"); + +// In this simple example, we just use one session for both producing and receiving + +ClientSessionFactory factory = locator.createClientSessionFactory(); +ClientSession session = factory.createSession(); + +// A producer is associated with an address ... + +ClientProducer producer = session.createProducer("example"); +ClientMessage message = session.createMessage(true); +message.getBodyBuffer().writeString("Hello"); + +// We need a queue attached to the address ... + +session.createQueue("example", RoutingType.ANYCAST, "example", true); + +// And a consumer attached to the queue ... + +ClientConsumer consumer = session.createConsumer("example"); + +// Once we have a queue, we can send the message ... + +producer.send(message); + +// We need to start the session before we can -receive- messages ... + +session.start(); +ClientMessage msgReceived = consumer.receive(); + +System.out.println("message = " + msgReceived.getBodyBuffer().readString()); + +session.close(); +``` diff --git a/docs/user-manual/en/critical-analysis.md b/docs/user-manual/en/critical-analysis.md index 18c17cdf413..40da82db8bd 100644 --- a/docs/user-manual/en/critical-analysis.md +++ b/docs/user-manual/en/critical-analysis.md @@ -22,7 +22,7 @@ You can use these following configuration options on broker.xml to configure how Name | Description -:--- | :--- +--- | --- critical-analyzer | Enable or disable the critical analysis (default true) critical-analyzer-timeout | Timeout used to do the critical analysis (default 120000 milliseconds) critical-analyzer-check-period | Time used to check the response times (default half of critical-analyzer-timeout) @@ -42,7 +42,7 @@ If you have critical-analyzer-policy=HALT [Artemis Critical Analyzer] 18:10:00,831 ERROR [org.apache.activemq.artemis.core.server] AMQ224079: The process for the virtual machine will be killed, as component org.apache.activemq.artemis.tests.integration.critical.CriticalSimpleTest$2@5af97850 is not responsive ``` -While if you have critical-analyzer-policy=SHUTDOWN +While if you have critical-analyzer-policy=`SHUTDOWN` ``` [Artemis Critical Analyzer] 18:07:53,475 ERROR [org.apache.activemq.artemis.core.server] AMQ224080: The server process will now be stopped, as component org.apache.activemq.artemis.tests.integration.critical.CriticalSimpleTest$2@5af97850 is not responsive @@ -85,7 +85,7 @@ AMQ119003: End Thread dump - The Server will be halted if configured to `HALT` -- The system will be stopped if `SHUTDOWN` is used: -* Notice that if the system is not behaving well, there is no guarantees the stop will work. +- The system will be stopped if `SHUTDOWN` is used. **Notice**: If the system + is not behaving well, there is no guarantees the stop will work. diff --git a/docs/user-manual/en/data-tools.md b/docs/user-manual/en/data-tools.md new file mode 100644 index 00000000000..fb7443fd9f9 --- /dev/null +++ b/docs/user-manual/en/data-tools.md @@ -0,0 +1,348 @@ +# Data Tools + +You can use the Artemis CLI to execute data maintenance tools: + +This is a list of sub-commands available + +Name | Description +---|--- +exp | Export the message data using a special and independent XML format +imp | Imports the journal to a running broker using the output from expt +data | Prints a report about journal records and summary of existent records, as well a report on paging +encode | shows an internal format of the journal encoded to String +decode | imports the internal journal format from encode + +You can use the help at the tool for more information on how to execute each of the tools. For example: + +``` +$ ./artemis help data print +NAME + artemis data print - Print data records information (WARNING: don't use + while a production server is running) + +SYNOPSIS + artemis data print [--bindings ] [--broker ] + [--f] [--jdbc] [--jdbc-bindings-table-name ] + [--jdbc-connection-url ] + [--jdbc-driver-class-name ] + [--jdbc-large-message-table-name ] + [--jdbc-message-table-name ] + [--jdbc-page-store-table-name ] [--journal ] + [--large-messages ] [--output ] + [--paging ] [--safe] [--verbose] [--] [] + +OPTIONS + --bindings + The folder used for bindings (default from broker.xml) + + --broker + This would override the broker configuration from the bootstrap + + --f + This will allow certain tools like print-data to be performed + ignoring any running servers. WARNING: Changing data concurrently + with a running broker may damage your data. Be careful with this + option. + + --jdbc + It will activate jdbc + + --jdbc-bindings-table-name + Name of the jdbc bindigns table + + --jdbc-connection-url + The connection used for the database + + --jdbc-driver-class-name + JDBC driver classname + + --jdbc-large-message-table-name + Name of the large messages table + + --jdbc-message-table-name + Name of the jdbc messages table + + --jdbc-page-store-table-name + Name of the page sotre messages table + + --journal + The folder used for messages journal (default from broker.xml) + + --large-messages + The folder used for large-messages (default from broker.xml) + + --output + Output name for the file + + --paging + The folder used for paging (default from broker.xml) + + --safe + It will print your data structure without showing your data + + --verbose + Adds more information on the execution + + -- + This option can be used to separate command-line options from the + list of argument, (useful when arguments might be mistaken for + command-line options + + + Broker Configuration URI, default + 'xml:${ARTEMIS_INSTANCE}/etc/bootstrap.xml' +``` + + +For a full list of data tools commands available use: + +``` +$ ./artemis help data +NAME + artemis data - data tools group (print|imp|exp|encode|decode|compact) + (example ./artemis data print) + +SYNOPSIS + artemis data + artemis data compact [--verbose] [--paging ] + [--journal ] [--large-messages ] + [--broker ] [--bindings ] + artemis data decode [--verbose] [--suffix ] [--paging ] + [--prefix ] [--file-size ] --input + [--journal ] [--directory ] + [--large-messages ] [--broker ] + [--bindings ] + artemis data encode [--verbose] [--directory ] + [--suffix ] [--paging ] [--prefix ] + [--file-size ] [--journal ] + [--large-messages ] [--broker ] + [--bindings ] + artemis data exp [--jdbc-bindings-table-name ] + [--jdbc-message-table-name ] [--paging ] + [--jdbc-connection-url ] + [--jdbc-large-message-table-name ] [--f] + [--large-messages ] [--broker ] + [--jdbc-page-store-table-name ] + [--jdbc-driver-class-name ] [--jdbc] [--verbose] + [--journal ] [--output ] [--bindings ] + artemis data imp [--user ] [--legacy-prefixes] [--verbose] + [--host ] [--port ] [--transaction] --input + [--password ] [--sort] + artemis data print [--jdbc-bindings-table-name ] + [--jdbc-message-table-name ] [--paging ] + [--jdbc-connection-url ] + [--jdbc-large-message-table-name ] [--f] + [--large-messages ] [--broker ] + [--jdbc-page-store-table-name ] + [--jdbc-driver-class-name ] [--safe] [--jdbc] [--verbose] + [--journal ] [--output ] [--bindings ] + +COMMANDS + With no arguments, Display help information + + print + Print data records information (WARNING: don't use while a + production server is running) + + With --jdbc-bindings-table-name option, Name of the jdbc bindigns + table + + With --jdbc-message-table-name option, Name of the jdbc messages + table + + With --paging option, The folder used for paging (default from + broker.xml) + + With --jdbc-connection-url option, The connection used for the + database + + With --jdbc-large-message-table-name option, Name of the large + messages table + + With --f option, This will allow certain tools like print-data to be + performed ignoring any running servers. WARNING: Changing data + concurrently with a running broker may damage your data. Be careful + with this option. + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --jdbc-page-store-table-name option, Name of the page sotre + messages table + + With --jdbc-driver-class-name option, JDBC driver classname + + With --safe option, It will print your data structure without + showing your data + + With --jdbc option, It will activate jdbc + + With --verbose option, Adds more information on the execution + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --output option, Output name for the file + + With --bindings option, The folder used for bindings (default from + broker.xml) + + exp + Export all message-data using an XML that could be interpreted by + any system. + + With --jdbc-bindings-table-name option, Name of the jdbc bindigns + table + + With --jdbc-message-table-name option, Name of the jdbc messages + table + + With --paging option, The folder used for paging (default from + broker.xml) + + With --jdbc-connection-url option, The connection used for the + database + + With --jdbc-large-message-table-name option, Name of the large + messages table + + With --f option, This will allow certain tools like print-data to be + performed ignoring any running servers. WARNING: Changing data + concurrently with a running broker may damage your data. Be careful + with this option. + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --jdbc-page-store-table-name option, Name of the page sotre + messages table + + With --jdbc-driver-class-name option, JDBC driver classname + + With --jdbc option, It will activate jdbc + + With --verbose option, Adds more information on the execution + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --output option, Output name for the file + + With --bindings option, The folder used for bindings (default from + broker.xml) + + imp + Import all message-data using an XML that could be interpreted by + any system. + + With --user option, User name used to import the data. (default + null) + + With --legacy-prefixes option, Do not remove prefixes from legacy + imports + + With --verbose option, Adds more information on the execution + + With --host option, The host used to import the data (default + localhost) + + With --port option, The port used to import the data (default 61616) + + With --transaction option, If this is set to true you will need a + whole transaction to commit at the end. (default false) + + With --input option, The input file name (default=exp.dmp) + + With --password option, User name used to import the data. (default + null) + + With --sort option, Sort the messages from the input (used for older + versions that won't sort messages) + + decode + Decode a journal's internal format into a new journal set of files + + With --verbose option, Adds more information on the execution + + With --suffix option, The journal suffix (default amq) + + With --paging option, The folder used for paging (default from + broker.xml) + + With --prefix option, The journal prefix (default activemq-data) + + With --file-size option, The journal size (default 10485760) + + With --input option, The input file name (default=exp.dmp) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --directory option, The journal folder (default journal folder + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + + encode + Encode a set of journal files into an internal encoded data format + + With --verbose option, Adds more information on the execution + + With --directory option, The journal folder (default the journal + folder from broker.xml) + + With --suffix option, The journal suffix (default amq) + + With --paging option, The folder used for paging (default from + broker.xml) + + With --prefix option, The journal prefix (default activemq-data) + + With --file-size option, The journal size (default 10485760) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + + compact + Compacts the journal of a non running server + + With --verbose option, Adds more information on the execution + + With --paging option, The folder used for paging (default from + broker.xml) + + With --journal option, The folder used for messages journal (default + from broker.xml) + + With --large-messages option, The folder used for large-messages + (default from broker.xml) + + With --broker option, This would override the broker configuration + from the bootstrap + + With --bindings option, The folder used for bindings (default from + broker.xml) + +``` \ No newline at end of file diff --git a/docs/user-manual/en/diverts.md b/docs/user-manual/en/diverts.md index 7408f4b73a1..5681e96f5cd 100644 --- a/docs/user-manual/en/diverts.md +++ b/docs/user-manual/en/diverts.md @@ -117,9 +117,9 @@ non-exclusive divert, again from the divert example: ```xml -
orders
- spyTopic - false +
orders
+ spyTopic + false
``` diff --git a/docs/user-manual/en/duplicate-detection.md b/docs/user-manual/en/duplicate-detection.md index c96c70224de..b3e95359779 100644 --- a/docs/user-manual/en/duplicate-detection.md +++ b/docs/user-manual/en/duplicate-detection.md @@ -48,7 +48,7 @@ already received a message with that value of the header. If it has received a message with the same value before then it will ignore the message. -> **Note** +> **Note:** > > Using duplicate detection to move messages between nodes can give you > the same *once and only once* delivery guarantees as if you were using @@ -118,7 +118,7 @@ configured by the parameter `persist-id-cache`, also in be persisted to permanent storage as they are received. The default value for this parameter is `true`. -> **Note** +> **Note:** > > When choosing a size of the duplicate id cache be sure to set it to a > larger enough size so if you resend messages all the previously sent diff --git a/docs/user-manual/en/embedding-activemq.md b/docs/user-manual/en/embedding-activemq.md index d309a58a4fd..3dcfb31f85c 100644 --- a/docs/user-manual/en/embedding-activemq.md +++ b/docs/user-manual/en/embedding-activemq.md @@ -1,29 +1,30 @@ # Embedding Apache ActiveMQ Artemis -Apache ActiveMQ Artemis is designed as set of simple Plain Old Java Objects (POJOs). -This means Apache ActiveMQ Artemis can be instantiated and run in any dependency -injection framework such as Spring or Google Guice. It also means that if you have an application that could use -messaging functionality internally, then it can *directly instantiate* -Apache ActiveMQ Artemis clients and servers in its own application code to perform that -functionality. We call this *embedding* Apache ActiveMQ Artemis. - -Examples of applications that might want to do this include any -application that needs very high performance, transactional, persistent -messaging but doesn't want the hassle of writing it all from scratch. - -Embedding Apache ActiveMQ Artemis can be done in very few easy steps. Instantiate the -configuration object, instantiate the server, start it, and you have a -Apache ActiveMQ Artemis running in your virtual machine. It's as simple and easy as -that. +Apache ActiveMQ Artemis is designed as set of simple Plain Old Java Objects +(POJOs). This means Apache ActiveMQ Artemis can be instantiated and run in any +dependency injection framework such as Spring or Google Guice. It also means +that if you have an application that could use messaging functionality +internally, then it can *directly instantiate* Apache ActiveMQ Artemis clients +and servers in its own application code to perform that functionality. We call +this *embedding* Apache ActiveMQ Artemis. + +Examples of applications that might want to do this include any application +that needs very high performance, transactional, persistent messaging but +doesn't want the hassle of writing it all from scratch. + +Embedding Apache ActiveMQ Artemis can be done in very few easy steps. +Instantiate the configuration object, instantiate the server, start it, and you +have a Apache ActiveMQ Artemis running in your virtual machine. It's as simple +and easy as that. ## Simple Config File Embedding -The simplest way to embed Apache ActiveMQ Artemis is to use the embedded wrapper -classes and configure Apache ActiveMQ Artemis through its configuration files. There -are two different helper classes for this depending on whether your -using the Apache ActiveMQ Artemis Core API or JMS. +The simplest way to embed Apache ActiveMQ Artemis is to use the embedded +wrapper classes and configure Apache ActiveMQ Artemis through its configuration +files. There are two different helper classes for this depending on whether +your using the Apache ActiveMQ Artemis Core API or JMS. -## Embeddeing Apache ActiveMQ Artemis Server +## Embedding an Apache ActiveMQ Artemis Broker For instantiating a core Apache ActiveMQ Artemis Server, the steps are pretty simple. The example requires that you have defined a configuration file @@ -38,9 +39,9 @@ EmbeddedActiveMQ embedded = new EmbeddedActiveMQ(); embedded.start(); -ClientSessionFactory nettyFactory = ActiveMQClient.createClientSessionFactory( - new TransportConfiguration( - InVMConnectorFactory.class.getName())); +ServerLocator serverLocator = ActiveMQClient.createServerLocator("vm://0"); + +ClientSessionFactory factory = serverLocator.createSessionFactory(); ClientSession session = factory.createSession(); @@ -65,23 +66,22 @@ System.out.println("message = " + msgReceived.getBody().readString()); session.close(); ``` -The `EmbeddedActiveMQ` class has a few additional setter methods that -allow you to specify a different config file name as well as other -properties. See the javadocs for this class for more details. +The `EmbeddedActiveMQ` class has a few additional setter methods that allow you +to specify a different config file name as well as other properties. See the +javadocs for this class for more details. ## POJO instantiation - Embedding Programmatically -You can follow this step-by-step guide to programmatically embed the -core, non-JMS Apache ActiveMQ Artemis Server instance: +You can follow this step-by-step guide to programmatically embed the core, +non-JMS Apache ActiveMQ Artemis Server instance: -Create the configuration object - this contains configuration -information for an Apache ActiveMQ Artemis instance. The setter methods of this class -allow you to programmatically set configuration options as describe in -the [Server Configuration](configuration-index.md) section. +Create the configuration object - this contains configuration information for +an Apache ActiveMQ Artemis instance. The setter methods of this class allow you +to programmatically set configuration options as describe in the [Server +Configuration](configuration-index.md) section. -The acceptors are configured through `ConfigurationImpl`. Just add the -`NettyAcceptorFactory` on the transports the same way you would through -the main configuration file. +The acceptors are configured through `Configuration`. Just add the acceptor URL +the same way you would through the main configuration file. ```java import org.apache.activemq.artemis.core.config.Configuration; @@ -90,12 +90,9 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; ... Configuration config = new ConfigurationImpl(); -HashSet transports = new HashSet(); - -transports.add(new TransportConfiguration(NettyAcceptorFactory.class.getName())); -transports.add(new TransportConfiguration(InVMAcceptorFactory.class.getName())); -config.setAcceptorConfigurations(transports); +config.addAcceptorConfiguration("in-vm", "vm://0"); +config.addAcceptorConfiguration("tcp", "tcp://127.0.0.1:61616"); ``` You need to instantiate an instance of @@ -123,8 +120,9 @@ server.start(); ## Dependency Frameworks -You may also choose to use a dependency injection framework such as -The Spring Framework. See [Spring Integration](spring-integration.md) for more details on +You may also choose to use a dependency injection framework such as The Spring +Framework. See [Spring Integration](spring-integration.md) for more details on Spring and Apache ActiveMQ Artemis. -Apache ActiveMQ Artemis standalone uses [Airline](https://github.com/airlift/airline) to bootstrap. +Apache ActiveMQ Artemis standalone uses +[Airline](https://github.com/airlift/airline) to bootstrap. diff --git a/docs/user-manual/en/examples.md b/docs/user-manual/en/examples.md index 1f76dd7c1ac..70d5ee0d8d8 100644 --- a/docs/user-manual/en/examples.md +++ b/docs/user-manual/en/examples.md @@ -1,10 +1,10 @@ -Examples -======== +# Examples -The Apache ActiveMQ Artemis distribution comes with over 90 run out-of-the-box examples demonstrating many of the features. +The Apache ActiveMQ Artemis distribution comes with over 90 run out-of-the-box +examples demonstrating many of the features. -The examples are available in both the binary and source distribution under the `examples` directory. Examples are split - by the following source tree: +The examples are available in both the binary and source distribution under the +`examples` directory. Examples are split by the following source tree: - features - Examples containing broker specific features. - clustered - examples showing load balancing and distribution capabilities. @@ -18,59 +18,70 @@ The examples are available in both the binary and source distribution under the - openwire - stomp -Running the Examples -===================== +## Running the Examples -To run any example, simply `cd` into the appropriate example directory and type `mvn verify` or `mvn install` (For -details please read the readme.html in each example directory). +To run any example, simply `cd` into the appropriate example directory and type +`mvn verify` or `mvn install` (For details please read the readme.html in each +example directory). -You can use the profile `-Pexamples` to run multiple examples under any example tree. +You can use the profile `-Pexamples` to run multiple examples under any example +tree. -For each server, you will have a created server under `./target/server0` (some examples use more than one server). +For each server, you will have a created server under `./target/server0` (some +examples use more than one server). -You have the option to prevent the example from starting the server (e.g. if you want to start the server manually) by -simply specifying the `-PnoServer` profile, e.g.: +You have the option to prevent the example from starting the server (e.g. if +you want to start the server manually) by simply specifying the `-PnoServer` +profile, e.g.: ```sh # running an example without running the server mvn verify -PnoServer ``` -Also under `./target` there will be a script repeating the commands to create each server. Here is the `create-server0.sh` -generated by the `Queue` example. This is useful to see exactly what command(s) are required to configure the server(s). +Also under `./target` there will be a script repeating the commands to create +each server. Here is the `create-server0.sh` generated by the `Queue` example. +This is useful to see exactly what command(s) are required to configure the +server(s). ```sh # These are the commands used to create server0 -/myInstallDirectory/apache-artemis-1.1.0/bin/artemis create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /myInstallDirectory/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 +/myInstallDirectory/apache-artemis/bin/artemis create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /myInstallDirectory/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 ``` -Several examples use UDP clustering which may not work in your environment by default. On linux the command would be: +Several examples use UDP clustering which may not work in your environment by +default. On linux the command would be: ```sh route add -net 224.0.0.0 netmask 240.0.0.0 dev lo ``` -This command should be run as root. This will redirect any traffic directed to `224.0.0.0` to the loopback interface. -On Mac OS X, the command is slightly different: +This command should be run as root. This will redirect any traffic directed to +`224.0.0.0` to the loopback interface. On Mac OS X, the command is slightly +different: -``` sh +```sh sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 ``` -All the examples use the [Maven plugin](maven-plugin.md), which can be useful for running your test servers as well. +All the examples use the [Maven plugin](maven-plugin.md), which can be useful +for running your test servers as well. -This is the common output when running an example. On this case taken from the `Queue` example: +This is the common output when running an example. On this case taken from the +`Queue` example: ```sh [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ -[INFO] Building ActiveMQ Artemis JMS Queue Example 1.1.0 +[INFO] Building ActiveMQ Artemis JMS Queue Example 2.5.0 [INFO] ------------------------------------------------------------------------ [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] -[INFO] --- maven-remote-resources-plugin:1.5:process (default) @ queue --- +[INFO] --- maven-remote-resources-plugin:1.5:process (process-resource-bundles) @ queue --- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ queue --- [INFO] Using 'UTF-8' encoding to copy filtered resources. @@ -78,17 +89,16 @@ This is the common output when running an example. On this case taken from the ` [INFO] Copying 3 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ queue --- -[INFO] Changes detected - recompiling the module! -[INFO] Compiling 1 source file to /work/apache-artemis-1.1.0/examples/features/standard/queue/target/classes +[INFO] Nothing to compile - all classes are up to date [INFO] -[INFO] --- maven-checkstyle-plugin:2.16:check (default) @ queue --- +[INFO] --- maven-checkstyle-plugin:2.17:check (default) @ queue --- [INFO] -[INFO] --- apache-rat-plugin:0.11:check (default) @ queue --- +[INFO] --- apache-rat-plugin:0.12:check (default) @ queue --- [INFO] RAT will not execute since it is configured to be skipped via system property 'rat.skip'. [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ queue --- [INFO] Using 'UTF-8' encoding to copy filtered resources. -[INFO] skip non existing resourceDirectory /work/apache-artemis-1.1.0/examples/features/standard/queue/src/test/resources +[INFO] skip non existing resourceDirectory /home/user/activemq-artemis/examples/features/standard/queue/src/test/resources [INFO] Copying 3 resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ queue --- @@ -97,815 +107,739 @@ This is the common output when running an example. On this case taken from the ` [INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ queue --- [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ queue --- -[INFO] Building jar: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/queue-1.1.0.jar +[INFO] Building jar: /home/user/activemq-artemis/examples/features/standard/queue/target/queue-2.5.0.jar [INFO] [INFO] --- maven-site-plugin:3.3:attach-descriptor (attach-descriptor) @ queue --- [INFO] [INFO] >>> maven-source-plugin:2.2.1:jar (attach-sources) > generate-sources @ queue >>> [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] [INFO] <<< maven-source-plugin:2.2.1:jar (attach-sources) < generate-sources @ queue <<< [INFO] +[INFO] [INFO] --- maven-source-plugin:2.2.1:jar (attach-sources) @ queue --- -[INFO] Building jar: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/queue-1.1.0-sources.jar +[INFO] Building jar: /home/user/activemq-artemis/examples/features/standard/queue/target/queue-2.5.0-sources.jar [INFO] [INFO] >>> maven-source-plugin:2.2.1:jar (default) > generate-sources @ queue >>> [INFO] +[INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-maven) @ queue --- +[INFO] [INFO] --- maven-enforcer-plugin:1.4:enforce (enforce-java) @ queue --- [INFO] [INFO] <<< maven-source-plugin:2.2.1:jar (default) < generate-sources @ queue <<< [INFO] +[INFO] [INFO] --- maven-source-plugin:2.2.1:jar (default) @ queue --- [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:create (create) @ queue --- +[INFO] --- dependency-check-maven:1.4.3:check (default) @ queue --- +[INFO] Skipping dependency-check +[INFO] +[INFO] --- artemis-maven-plugin:2.5.0:create (create) @ queue --- [INFO] Local id: local - url: file:///Users/apacheuser/.m2/repository/ + url: file:///home/user/.m2/repository/ layout: default snapshots: [enabled => true, update => always] releases: [enabled => true, update => always] [INFO] Entries.size 2 -[INFO] ... key=project = MavenProject: org.apache.activemq.examples.broker:queue:1.1.0 @ /work/apache-artemis-1.1.0/examples/features/standard/queue/pom.xml -[INFO] ... key=pluginDescriptor = Component Descriptor: role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCLIPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:cli' -role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCreatePlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:create' -role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisClientPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:1.1.0:runClient' +[INFO] ... key=project = MavenProject: org.apache.activemq.examples.broker:queue:2.5.0 @ /home/user/activemq-artemis/examples/features/standard/queue/pom.xml +[INFO] ... key=pluginDescriptor = Component Descriptor: role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCLIPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:cli' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisCreatePlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:create' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisDependencyScanPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:dependency-scan' +role: 'org.apache.maven.plugin.Mojo', implementation: 'org.apache.activemq.artemis.maven.ArtemisClientPlugin', role hint: 'org.apache.activemq:artemis-maven-plugin:2.5.0:runClient' --- -Executing org.apache.activemq.artemis.cli.commands.Create create --allow-anonymous --silent --force --no-web --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-autotune --verbose /work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 -Home::/work/apache-artemis-1.1.0/examples/features/standard/queue/../../../.., Instance::. -Creating ActiveMQ Artemis instance at: /work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0 +Executing org.apache.activemq.artemis.cli.commands.Create create --allow-anonymous --silent --force --user guest --password guest --role guest --port-offset 0 --data ./data --allow-anonymous --no-web --no-autotune --verbose --aio /home/user/activemq-artemis/examples/features/standard/queue/target/server0 +Home::/home/user/activemq-artemis/examples/features/standard/queue/../../../../artemis-distribution/target/apache-artemis-2.5.0-bin/apache-artemis-2.5.0, Instance::null +Creating ActiveMQ Artemis instance at: /home/user/activemq-artemis/examples/features/standard/queue/target/server0 You can now start the broker by executing: - "/work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0/bin/artemis" run + "/home/user/activemq-artemis/examples/features/standard/queue/target/server0/bin/artemis" run Or you can run the broker in the background using: - "/work/apache-artemis-1.1.0/examples/features/standard/queue/target/server0/bin/artemis-service" start + "/home/user/activemq-artemis/examples/features/standard/queue/target/server0/bin/artemis-service" start [INFO] ################################################################################################### [INFO] create-server0.sh created with commands to reproduce server0 -[INFO] under /work/apache-artemis-1.1.0/examples/features/standard/queue/target +[INFO] under /home/user/activemq-artemis/examples/features/standard/queue/target [INFO] ################################################################################################### [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:cli (start) @ queue --- -[INFO] awaiting server to start +[INFO] --- artemis-maven-plugin:2.5.0:cli (start) @ queue --- [INFO] awaiting server to start server-out: _ _ _ server-out: / \ ____| |_ ___ __ __(_) _____ server-out: / _ \| _ \ __|/ _ \ \/ | |/ __/ server-out: / ___ \ | \/ |_/ __/ |\/| | |\___ \ server-out: /_/ \_\| \__\____|_| |_|_|/___ / -server-out: Apache ActiveMQ Artemis 1.1.0 +server-out: Apache ActiveMQ Artemis 2.5.0 server-out: server-out: -server-out:17:30:25,091 INFO [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server -server-out:17:30:25,120 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=./data/journal,bindingsDirectory=./data/bindings,largeMessagesDirectory=./data/large-messages,pagingDirectory=./data/paging) -server-out:17:30:25,152 INFO [org.apache.activemq.artemis.core.server] AMQ221013: Using NIO Journal -server-out:17:30:25,195 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE -server-out:17:30:25,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-amqp-protocol]. Adding protocol support for: AMQP -server-out:17:30:25,209 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-hornetq-protocol]. Adding protocol support for: HORNETQ -server-out:17:30:25,211 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-mqtt-protocol]. Adding protocol support for: MQTT -server-out:17:30:25,214 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-openwire-protocol]. Adding protocol support for: OPENWIRE -server-out:17:30:25,335 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-stomp-protocol]. Adding protocol support for: STOMP +server-out:2018-03-13 09:06:37,980 WARN [org.apache.activemq.artemis.core.server] AMQ222018: AIO was not located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO to enable the AIO journal +server-out:2018-03-13 09:06:38,052 INFO [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server +[INFO] awaiting server to start +server-out:2018-03-13 09:06:38,123 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=./data/journal,bindingsDirectory=./data/bindings,largeMessagesDirectory=./data/large-messages,pagingDirectory=./data/paging) +server-out:2018-03-13 09:06:38,146 INFO [org.apache.activemq.artemis.core.server] AMQ221013: Using NIO Journal +server-out:2018-03-13 09:06:38,178 INFO [org.apache.activemq.artemis.core.server] AMQ221057: Global Max Size is being adjusted to 1/2 of the JVM max size (-Xmx). being defined as 1,073,741,824 +server-out:2018-03-13 09:06:38,197 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-amqp-protocol]. Adding protocol support for: AMQP +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-hornetq-protocol]. Adding protocol support for: HORNETQ +server-out:2018-03-13 09:06:38,198 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-mqtt-protocol]. Adding protocol support for: MQTT +server-out:2018-03-13 09:06:38,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-openwire-protocol]. Adding protocol support for: OPENWIRE +server-out:2018-03-13 09:06:38,199 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-stomp-protocol]. Adding protocol support for: STOMP +server-out:2018-03-13 09:06:38,261 INFO [org.apache.activemq.artemis.core.server] AMQ221034: Waiting indefinitely to obtain live lock +server-out:2018-03-13 09:06:38,262 INFO [org.apache.activemq.artemis.core.server] AMQ221035: Live Server Obtained live lock +server-out:2018-03-13 09:06:38,386 INFO [org.apache.activemq.artemis.core.server] AMQ221003: Deploying queue DLQ on address DLQ +server-out:2018-03-13 09:06:38,445 INFO [org.apache.activemq.artemis.core.server] AMQ221003: Deploying queue ExpiryQueue on address ExpiryQueue [INFO] awaiting server to start -server-out:17:30:25,781 INFO [org.apache.activemq.artemis.core.server] AMQ221003: trying to deploy queue jms.queue.DLQ -server-out:17:30:25,835 INFO [org.apache.activemq.artemis.core.server] AMQ221003: trying to deploy queue jms.queue.ExpiryQueue -server-out:17:30:25,933 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:61616 for protocols [CORE,MQTT,AMQP,HORNETQ,STOMP,OPENWIRE] -server-out:17:30:25,936 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:5445 for protocols [HORNETQ,STOMP] -server-out:17:30:25,939 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:5672 for protocols [AMQP] -server-out:17:30:25,944 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:1883 for protocols [MQTT] -server-out:17:30:25,948 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started Acceptor at 0.0.0.0:61613 for protocols [STOMP] -server-out:17:30:25,949 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live -server-out:17:30:25,949 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.1.0 [nodeID=a855176b-50f0-11e5-937e-2fe9bb000966] +server-out:2018-03-13 09:06:38,739 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:61616 for protocols [CORE,MQTT,AMQP,STOMP,HORNETQ,OPENWIRE] +server-out:2018-03-13 09:06:38,741 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:5445 for protocols [HORNETQ,STOMP] +server-out:2018-03-13 09:06:38,742 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:5672 for protocols [AMQP] +server-out:2018-03-13 09:06:38,744 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:1883 for protocols [MQTT] +server-out:2018-03-13 09:06:38,746 INFO [org.apache.activemq.artemis.core.server] AMQ221020: Started EPOLL Acceptor at 0.0.0.0:61613 for protocols [STOMP] +server-out:2018-03-13 09:06:38,752 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live +server-out:2018-03-13 09:06:38,752 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 2.5.0 [0.0.0.0, nodeID=bf1853a1-26c7-11e8-9378-d96702a756ed] [INFO] Server started [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:runClient (runClient) @ queue --- +[INFO] --- artemis-maven-plugin:2.5.0:runClient (runClient) @ queue --- Sent message: This is a text message Received message: This is a text message [INFO] -[INFO] --- artemis-maven-plugin:1.1.0:cli (stop) @ queue --- -server-out:17:30:27,476 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.0.1-SNA +[INFO] --- artemis-maven-plugin:2.5.0:cli (stop) @ queue --- +server-out:2018-03-13 09:06:40,888 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 2.5.0 [bf1853a1-26c7-11e8-9378-d96702a756ed] stopped, uptime 2.786 seconds +server-out:Server stopped! [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ -[INFO] Total time: 7.840 s -[INFO] Finished at: 2015-09-01T17:30:27-04:00 -[INFO] Final Memory: 42M/508M +[INFO] Total time: 6.641 s +[INFO] Finished at: 2018-03-13T09:06:40-05:00 +[INFO] Final Memory: 43M/600M [INFO] ------------------------------------------------------------------------ ``` -List -==== - -This includes a preview list of a few examples that we distribute with Artemis. Please refer to the distribution for a more accurate list. +This includes a preview list of a few examples that we distribute with Artemis. +Please refer to the distribution for a more accurate list. -Applet ------- +## Applet This example shows you how to send and receive JMS messages from an Applet. -Application-Layer Failover --------------------------- +## Application-Layer Failover -Apache ActiveMQ Artemis also supports Application-Layer failover, useful in the case -that replication is not enabled on the server side. +Apache ActiveMQ Artemis also supports Application-Layer failover, useful in the +case that replication is not enabled on the server side. -With Application-Layer failover, it's up to the application to register -a JMS `ExceptionListener` with Apache ActiveMQ Artemis which will be called by Apache ActiveMQ Artemis -in the event that connection failure is detected. +With Application-Layer failover, it's up to the application to register a JMS +`ExceptionListener` with Apache ActiveMQ Artemis which will be called by Apache +ActiveMQ Artemis in the event that connection failure is detected. -The code in the `ExceptionListener` then recreates the JMS connection, -session, etc on another node and the application can continue. +The code in the `ExceptionListener` then recreates the JMS connection, session, +etc on another node and the application can continue. -Application-layer failover is an alternative approach to High -Availability (HA). Application-layer failover differs from automatic -failover in that some client side coding is required in order to -implement this. Also, with Application-layer failover, since the old -session object dies and a new one is created, any uncommitted work in -the old session will be lost, and any unacknowledged messages might be -redelivered. +Application-layer failover is an alternative approach to High Availability +(HA). Application-layer failover differs from automatic failover in that some +client side coding is required in order to implement this. Also, with +Application-layer failover, since the old session object dies and a new one is +created, any uncommitted work in the old session will be lost, and any +unacknowledged messages might be redelivered. -Core Bridge Example -------------------- +## Core Bridge Example -The `bridge` example demonstrates a core bridge deployed on one server, -which consumes messages from a local queue and forwards them to an -address on a second server. +The `bridge` example demonstrates a core bridge deployed on one server, which +consumes messages from a local queue and forwards them to an address on a +second server. -Core bridges are used to create message flows between any two Apache ActiveMQ Artemis -servers which are remotely separated. Core bridges are resilient and -will cope with temporary connection failure allowing them to be an ideal -choice for forwarding over unreliable connections, e.g. a WAN. +Core bridges are used to create message flows between any two Apache ActiveMQ +Artemis servers which are remotely separated. Core bridges are resilient and +will cope with temporary connection failure allowing them to be an ideal choice +for forwarding over unreliable connections, e.g. a WAN. -Browser -------- +## Browser -The `browser` example shows you how to use a JMS `QueueBrowser` with -Apache ActiveMQ Artemis. +The `browser` example shows you how to use a JMS `QueueBrowser` with Apache +ActiveMQ Artemis. -Queues are a standard part of JMS, please consult the JMS 2.0 -specification for full details. +Queues are a standard part of JMS, please consult the JMS 2.0 specification for +full details. -A `QueueBrowser` is used to look at messages on the queue without -removing them. It can scan the entire content of a queue or only -messages matching a message selector. +A `QueueBrowser` is used to look at messages on the queue without removing +them. It can scan the entire content of a queue or only messages matching a +message selector. -Client Kickoff --------------- +## Client Kickoff -The `client-kickoff` example shows how to terminate client connections -given an IP address using the JMX management API. +The `client-kickoff` example shows how to terminate client connections given an +IP address using the JMX management API. -Client side failover listener ------------------------------ +## Client side failover listener -The `client-side-failoverlistener` example shows how to register a -listener to monitor failover events +The `client-side-failoverlistener` example shows how to register a listener to +monitor failover events -Client-Side Load-Balancing --------------------------- +## Client-Side Load-Balancing -The `client-side-load-balancing` example demonstrates how sessions -created from a single JMS `Connection` can be created to different nodes -of the cluster. In other words it demonstrates how Apache ActiveMQ Artemis does -client-side load-balancing of sessions across the cluster. +The `client-side-load-balancing` example demonstrates how sessions created from +a single JMS `Connection` can be created to different nodes of the cluster. In +other words it demonstrates how Apache ActiveMQ Artemis does client-side +load-balancing of sessions across the cluster. -Clustered Durable Subscription ------------------------------- +## Clustered Durable Subscription This example demonstrates a clustered JMS durable subscription -Clustered Grouping ------------------- +## Clustered Grouping -This is similar to the message grouping example except that it -demonstrates it working over a cluster. Messages sent to different nodes -with the same group id will be sent to the same node and the same -consumer. +This is similar to the message grouping example except that it demonstrates it +working over a cluster. Messages sent to different nodes with the same group id +will be sent to the same node and the same consumer. -Clustered Queue ---------------- +## Clustered Queue -The `clustered-queue` example demonstrates a queue deployed on two -different nodes. The two nodes are configured to form a cluster. We then -create a consumer for the queue on each node, and we create a producer -on only one of the nodes. We then send some messages via the producer, -and we verify that both consumers receive the sent messages in a -round-robin fashion. +The `clustered-queue` example demonstrates a queue deployed on two different +nodes. The two nodes are configured to form a cluster. We then create a +consumer for the queue on each node, and we create a producer on only one of +the nodes. We then send some messages via the producer, and we verify that both +consumers receive the sent messages in a round-robin fashion. -Clustering with JGroups ------------------------ +## Clustering with JGroups -The `clustered-jgroups` example demonstrates how to form a two node -cluster using JGroups as its underlying topology discovery technique, -rather than the default UDP broadcasting. We then create a consumer for -the queue on each node, and we create a producer on only one of the -nodes. We then send some messages via the producer, and we verify that -both consumers receive the sent messages in a round-robin fashion. +The `clustered-jgroups` example demonstrates how to form a two node cluster +using JGroups as its underlying topology discovery technique, rather than the +default UDP broadcasting. We then create a consumer for the queue on each node, +and we create a producer on only one of the nodes. We then send some messages +via the producer, and we verify that both consumers receive the sent messages +in a round-robin fashion. -Clustered Standalone --------------------- +## Clustered Standalone -The `clustered-standalone` example demonstrates how to configure and -starts 3 cluster nodes on the same machine to form a cluster. A -subscriber for a JMS topic is created on each node, and we create a -producer on only one of the nodes. We then send some messages via the -producer, and we verify that the 3 subscribers receive all the sent -messages. +The `clustered-standalone` example demonstrates how to configure and starts 3 +cluster nodes on the same machine to form a cluster. A subscriber for a JMS +topic is created on each node, and we create a producer on only one of the +nodes. We then send some messages via the producer, and we verify that the 3 +subscribers receive all the sent messages. -Clustered Static Discovery --------------------------- +## Clustered Static Discovery -This example demonstrates how to configure a cluster using a list of -connectors rather than UDP for discovery +This example demonstrates how to configure a cluster using a list of connectors +rather than UDP for discovery -Clustered Static Cluster One Way --------------------------------- +## Clustered Static Cluster One Way -This example demonstrates how to set up a cluster where cluster -connections are one way, i.e. server A -\> Server B -\> Server C +This example demonstrates how to set up a cluster where cluster connections are +one way, i.e. server A -\> Server B -\> Server C -Clustered Topic ---------------- +## Clustered Topic The `clustered-topic` example demonstrates a JMS topic deployed on two -different nodes. The two nodes are configured to form a cluster. We then -create a subscriber on the topic on each node, and we create a producer -on only one of the nodes. We then send some messages via the producer, -and we verify that both subscribers receive all the sent messages. +different nodes. The two nodes are configured to form a cluster. We then create +a subscriber on the topic on each node, and we create a producer on only one of +the nodes. We then send some messages via the producer, and we verify that both +subscribers receive all the sent messages. -Message Consumer Rate Limiting ------------------------------- +## Message Consumer Rate Limiting -With Apache ActiveMQ Artemis you can specify a maximum consume rate at which a JMS -MessageConsumer will consume messages. This can be specified when -creating or deploying the connection factory. +With Apache ActiveMQ Artemis you can specify a maximum consume rate at which a +JMS MessageConsumer will consume messages. This can be specified when creating +or deploying the connection factory. -If this value is specified then Apache ActiveMQ Artemis will ensure that messages are -never consumed at a rate higher than the specified rate. This is a form -of consumer throttling. +If this value is specified then Apache ActiveMQ Artemis will ensure that +messages are never consumed at a rate higher than the specified rate. This is a +form of consumer throttling. -Dead Letter ------------ +## Dead Letter -The `dead-letter` example shows you how to define and deal with dead -letter messages. Messages can be delivered unsuccessfully (e.g. if the -transacted session used to consume them is rolled back). +The `dead-letter` example shows you how to define and deal with dead letter +messages. Messages can be delivered unsuccessfully (e.g. if the transacted +session used to consume them is rolled back). Such a message goes back to the JMS destination ready to be redelivered. -However, this means it is possible for a message to be delivered again -and again without any success and remain in the destination, clogging -the system. +However, this means it is possible for a message to be delivered again and +again without any success and remain in the destination, clogging the system. To prevent this, messaging systems define dead letter messages: after a -specified unsuccessful delivery attempts, the message is removed from -the destination and put instead in a dead letter destination where they -can be consumed for further investigation. +specified unsuccessful delivery attempts, the message is removed from the +destination and put instead in a dead letter destination where they can be +consumed for further investigation. -Delayed Redelivery ------------------- +## Delayed Redelivery -The `delayed-redelivery` example demonstrates how Apache ActiveMQ Artemis can be -configured to provide a delayed redelivery in the case a message needs -to be redelivered. +The `delayed-redelivery` example demonstrates how Apache ActiveMQ Artemis can +be configured to provide a delayed redelivery in the case a message needs to be +redelivered. -Delaying redelivery can often be useful in the case that clients -regularly fail or roll-back. Without a delayed redelivery, the system -can get into a "thrashing" state, with delivery being attempted, the -client rolling back, and delivery being re-attempted in quick -succession, using up valuable CPU and network resources. +Delaying redelivery can often be useful in the case that clients regularly fail +or roll-back. Without a delayed redelivery, the system can get into a +"thrashing" state, with delivery being attempted, the client rolling back, and +delivery being re-attempted in quick succession, using up valuable CPU and +network resources. -Divert ------- +## Divert -Apache ActiveMQ Artemis diverts allow messages to be transparently "diverted" or copied -from one address to another with just some simple configuration defined -on the server side. +Apache ActiveMQ Artemis diverts allow messages to be transparently "diverted" +or copied from one address to another with just some simple configuration +defined on the server side. -Durable Subscription --------------------- +## Durable Subscription -The `durable-subscription` example shows you how to use a durable -subscription with Apache ActiveMQ Artemis. Durable subscriptions are a standard part of -JMS, please consult the JMS 1.1 specification for full details. +The `durable-subscription` example shows you how to use a durable subscription +with Apache ActiveMQ Artemis. Durable subscriptions are a standard part of JMS, +please consult the JMS 1.1 specification for full details. -Unlike non-durable subscriptions, the key function of durable -subscriptions is that the messages contained in them persist longer than -the lifetime of the subscriber - i.e. they will accumulate messages sent -to the topic even if there is no active subscriber on them. They will -also survive server restarts or crashes. Note that for the messages to -be persisted, the messages sent to them must be marked as durable -messages. +Unlike non-durable subscriptions, the key function of durable subscriptions is +that the messages contained in them persist longer than the lifetime of the +subscriber - i.e. they will accumulate messages sent to the topic even if there +is no active subscriber on them. They will also survive server restarts or +crashes. Note that for the messages to be persisted, the messages sent to them +must be marked as durable messages. -Embedded --------- +## Embedded The `embedded` example shows how to embed a broker within your own code using POJO instantiation and no config files. -Embedded Simple ---------------- +## Embedded Simple The `embedded-simple` example shows how to embed a broker within your own code using regular Apache ActiveMQ Artemis XML files. -Exclusive Queue ---------------- +## Exclusive Queue -The `exlusive-queue` example shows you how to use Exclusive Queues, that -route all messages to only one consumer at a time. +The `exlusive-queue` example shows you how to use exclusive queues, that route +all messages to only one consumer at a time. -Message Expiration ------------------- +## Message Expiration -The `expiry` example shows you how to define and deal with message -expiration. Messages can be retained in the messaging system for a -limited period of time before being removed. JMS specification states -that clients should not receive messages that have been expired (but it -does not guarantee this will not happen). +The `expiry` example shows you how to define and deal with message expiration. +Messages can be retained in the messaging system for a limited period of time +before being removed. JMS specification states that clients should not receive +messages that have been expired (but it does not guarantee this will not +happen). -Apache ActiveMQ Artemis can assign an expiry address to a given queue so that when -messages are expired, they are removed from the queue and sent to the -expiry address. These "expired" messages can later be consumed from the -expiry address for further inspection. +Apache ActiveMQ Artemis can assign an expiry address to a given queue so that +when messages are expired, they are removed from the queue and sent to the +expiry address. These "expired" messages can later be consumed from the expiry +address for further inspection. -Apache ActiveMQ Artemis Resource Adapter example ---------------------------------- +## Apache ActiveMQ Artemis Resource Adapter example -This examples shows how to build the activemq resource adapters a rar -for deployment in other Application Server's +This examples shows how to build the activemq resource adapters a rar for +deployment in other Application Server's -HTTP Transport --------------- +## HTTP Transport -The `http-transport` example shows you how to configure Apache ActiveMQ Artemis to use -the HTTP protocol as its transport layer. +The `http-transport` example shows you how to configure Apache ActiveMQ Artemis +to use the HTTP protocol as its transport layer. -Instantiate JMS Objects Directly --------------------------------- +## Instantiate JMS Objects Directly -Usually, JMS Objects such as `ConnectionFactory`, `Queue` and `Topic` -instances are looked up from JNDI before being used by the client code. -This objects are called "administered objects" in JMS terminology. +Usually, JMS Objects such as `ConnectionFactory`, `Queue` and `Topic` instances +are looked up from JNDI before being used by the client code. This objects are +called "administered objects" in JMS terminology. -However, in some cases a JNDI server may not be available or desired. To -come to the rescue Apache ActiveMQ Artemis also supports the direct instantiation of -these administered objects on the client side so you don't have to use -JNDI for JMS. +However, in some cases a JNDI server may not be available or desired. To come +to the rescue Apache ActiveMQ Artemis also supports the direct instantiation of +these administered objects on the client side so you don't have to use JNDI for +JMS. -Interceptor ------------ +## Interceptor -Apache ActiveMQ Artemis allows an application to use an interceptor to hook into the -messaging system. Interceptors allow you to handle various message +Apache ActiveMQ Artemis allows an application to use an interceptor to hook +into the messaging system. Interceptors allow you to handle various message events in Apache ActiveMQ Artemis. -JAAS ----- +## Interceptor AMQP -The `jaas` example shows you how to configure Apache ActiveMQ Artemis to use JAAS for -security. Apache ActiveMQ Artemis can leverage JAAS to delegate user authentication and -authorization to existing security infrastructure. +Similar to the [Interceptor](#interceptor) example, but using AMQP interceptors. -JMS Auto Closable ------------------ +## Interceptor Client -The `jms-auto-closeable` example shows how JMS resources, such as -connections, sessions and consumers, in JMS 2 can be automatically -closed on error. +Similar to the [Interceptor](#interceptor) example, but using interceptors on +the **client** rather than the broker. -JMS Completion Listener ------------------------ +## Interceptor MQTT + +Similar to the [Interceptor](#interceptor) example, but using MQTT interceptors. + +## JAAS + +The `jaas` example shows you how to configure Apache ActiveMQ Artemis to use +JAAS for security. Apache ActiveMQ Artemis can leverage JAAS to delegate user +authentication and authorization to existing security infrastructure. + +## JMS Auto Closable + +The `jms-auto-closeable` example shows how JMS resources, such as connections, +sessions and consumers, in JMS 2 can be automatically closed on error. + +## JMS Completion Listener The `jms-completion-listener` example shows how to send a message -asynchronously to Apache ActiveMQ Artemis and use a CompletionListener to be notified -of the Broker receiving it. +asynchronously to Apache ActiveMQ Artemis and use a CompletionListener to be +notified of the Broker receiving it. -JMS Bridge ----------- +## JMS Bridge -The `jms-bridge` example shows how to setup a bridge between two -standalone Apache ActiveMQ Artemis servers. +The `jms-bridge` example shows how to setup a bridge between two standalone +Apache ActiveMQ Artemis servers. -JMS Context ------------ +## JMS Context The `jms-context` example shows how to send and receive a message to/from an address/queue using Apache ActiveMQ Artemis by using a JMS Context. -A JMSContext is part of JMS 2.0 and combines the JMS Connection and -Session Objects into a simple Interface. +A JMSContext is part of JMS 2.0 and combines the JMS Connection and Session +Objects into a simple Interface. -JMS Shared Consumer -------------------- +## JMS Shared Consumer -The `jms-shared-consumer` example shows you how can use shared consumers -to share a subscription on a topic. In JMS 1.1 this was not allowed and -so caused a scalability issue. In JMS 2 this restriction has been lifted -so you can share the load across different threads and connections. +The `jms-shared-consumer` example shows you how can use shared consumers to +share a subscription on a topic. In JMS 1.1 this was not allowed and so caused +a scalability issue. In JMS 2 this restriction has been lifted so you can share +the load across different threads and connections. -JMX Management --------------- +## JMX Management The `jmx` example shows how to manage Apache ActiveMQ Artemis using JMX. -Large Message -------------- +## Large Message The `large-message` example shows you how to send and receive very large -messages with Apache ActiveMQ Artemis. Apache ActiveMQ Artemis supports the sending and receiving of -huge messages, much larger than can fit in available RAM on the client -or server. Effectively the only limit to message size is the amount of -disk space you have on the server. +messages with Apache ActiveMQ Artemis. Apache ActiveMQ Artemis supports the +sending and receiving of huge messages, much larger than can fit in available +RAM on the client or server. Effectively the only limit to message size is the +amount of disk space you have on the server. Large messages are persisted on the server so they can survive a server -restart. In other words Apache ActiveMQ Artemis doesn't just do a simple socket stream -from the sender to the consumer. +restart. In other words Apache ActiveMQ Artemis doesn't just do a simple socket +stream from the sender to the consumer. -Last-Value Queue ----------------- +## Last-Value Queue -The `last-value-queue` example shows you how to define and deal with -last-value queues. Last-value queues are special queues which discard -any messages when a newer message with the same value for a well-defined -last-value property is put in the queue. In other words, a last-value -queue only retains the last value. +The `last-value-queue` example shows you how to define and deal with last-value +queues. Last-value queues are special queues which discard any messages when a +newer message with the same value for a well-defined last-value property is put +in the queue. In other words, a last-value queue only retains the last value. -A typical example for last-value queue is for stock prices, where you -are only interested by the latest price for a particular stock. +A typical example for last-value queue is for stock prices, where you are only +interested by the latest price for a particular stock. -Management ----------- +## Management -The `management` example shows how to manage Apache ActiveMQ Artemis using JMS Messages -to invoke management operations on the server. +The `management` example shows how to manage Apache ActiveMQ Artemis using JMS +Messages to invoke management operations on the server. -Management Notification ------------------------ +## Management Notification The `management-notification` example shows how to receive management -notifications from Apache ActiveMQ Artemis using JMS messages. Apache ActiveMQ Artemis servers emit -management notifications when events of interest occur (consumers are -created or closed, addresses are created or deleted, security +notifications from Apache ActiveMQ Artemis using JMS messages. Apache ActiveMQ +Artemis servers emit management notifications when events of interest occur +(consumers are created or closed, addresses are created or deleted, security authentication fails, etc.). -Message Counter ---------------- +## Message Counter -The `message-counters` example shows you how to use message counters to -obtain message information for a queue. +The `message-counters` example shows you how to use message counters to obtain +message information for a queue. -Message Group -------------- +## Message Group -The `message-group` example shows you how to configure and use message -groups with Apache ActiveMQ Artemis. Message groups allow you to pin messages so they -are only consumed by a single consumer. Message groups are sets of -messages that has the following characteristics: +The `message-group` example shows you how to configure and use message groups +with Apache ActiveMQ Artemis. Message groups allow you to pin messages so they +are only consumed by a single consumer. Message groups are sets of messages +that has the following characteristics: -- Messages in a message group share the same group id, i.e. they have - same JMSXGroupID string property values +- Messages in a message group share the same group id, i.e. they have same + JMSXGroupID string property values -- The consumer that receives the first message of a group will receive - all the messages that belongs to the group +- The consumer that receives the first message of a group will receive all the + messages that belongs to the group -Message Group -------------- +## Message Group -The `message-group2` example shows you how to configure and use message -groups with Apache ActiveMQ Artemis via a connection factory. +The `message-group2` example shows you how to configure and use message groups +with Apache ActiveMQ Artemis via a connection factory. -Message Priority ----------------- +## Message Priority -Message Priority can be used to influence the delivery order for -messages. +Message Priority can be used to influence the delivery order for messages. -It can be retrieved by the message's standard header field 'JMSPriority' -as defined in JMS specification version 1.1. +It can be retrieved by the message's standard header field 'JMSPriority' as +defined in JMS specification version 1.1. -The value is of type integer, ranging from 0 (the lowest) to 9 (the -highest). When messages are being delivered, their priorities will -effect their order of delivery. Messages of higher priorities will -likely be delivered before those of lower priorities. +The value is of type integer, ranging from 0 (the lowest) to 9 (the highest). +When messages are being delivered, their priorities will effect their order of +delivery. Messages of higher priorities will likely be delivered before those +of lower priorities. Messages of equal priorities are delivered in the natural order of their -arrival at their destinations. Please consult the JMS 1.1 specification -for full details. +arrival at their destinations. Please consult the JMS 1.1 specification for +full details. -Multiple Failover ------------------ +## Multiple Failover -This example demonstrates how to set up a live server with multiple -backups +This example demonstrates how to set up a live server with multiple backups -Multiple Failover Failback --------------------------- +## Multiple Failover Failback -This example demonstrates how to set up a live server with multiple -backups but forcing failover back to the original live server +This example demonstrates how to set up a live server with multiple backups but +forcing failover back to the original live server -No Consumer Buffering ---------------------- +## No Consumer Buffering -By default, Apache ActiveMQ Artemis consumers buffer messages from the server in a -client side buffer before you actually receive them on the client side. -This improves performance since otherwise every time you called -receive() or had processed the last message in a -`MessageListener onMessage()` method, the Apache ActiveMQ Artemis client would have to -go the server to request the next message, which would then get sent to -the client side, if one was available. +By default, Apache ActiveMQ Artemis consumers buffer messages from the server +in a client side buffer before you actually receive them on the client side. +This improves performance since otherwise every time you called receive() or +had processed the last message in a `MessageListener onMessage()` method, the +Apache ActiveMQ Artemis client would have to go the server to request the next +message, which would then get sent to the client side, if one was available. This would involve a network round trip for every message and reduce -performance. Therefore, by default, Apache ActiveMQ Artemis pre-fetches messages into a -buffer on each consumer. +performance. Therefore, by default, Apache ActiveMQ Artemis pre-fetches +messages into a buffer on each consumer. -In some case buffering is not desirable, and Apache ActiveMQ Artemis allows it to be -switched off. This example demonstrates that. +In some case buffering is not desirable, and Apache ActiveMQ Artemis allows it +to be switched off. This example demonstrates that. -Non-Transaction Failover With Server Data Replication ------------------------------------------------------ +## Non-Transaction Failover With Server Data Replication -The `non-transaction-failover` example demonstrates two servers coupled -as a live-backup pair for high availability (HA), and a client using a -*non-transacted* JMS session failing over from live to backup when the -live server is crashed. +The `non-transaction-failover` example demonstrates two servers coupled as a +live-backup pair for high availability (HA), and a client using a +*non-transacted* JMS session failing over from live to backup when the live +server is crashed. -Apache ActiveMQ Artemis implements failover of client connections between live and -backup servers. This is implemented by the replication of state between -live and backup nodes. When replication is configured and a live node -crashes, the client connections can carry and continue to send and -consume messages. When non-transacted sessions are used, once and only -once message delivery is not guaranteed and it is possible that some -messages will be lost or delivered twice. +Apache ActiveMQ Artemis implements failover of client connections between live +and backup servers. This is implemented by the replication of state between +live and backup nodes. When replication is configured and a live node crashes, +the client connections can carry and continue to send and consume messages. +When non-transacted sessions are used, once and only once message delivery is +not guaranteed and it is possible that some messages will be lost or delivered +twice. -OpenWire --------- +## OpenWire -The `Openwire` example shows how to configure an Apache ActiveMQ Artemis server to -communicate with an Apache ActiveMQ Artemis JMS client that uses open-wire protocol. +The `Openwire` example shows how to configure an Apache ActiveMQ Artemis server +to communicate with an Apache ActiveMQ Artemis JMS client that uses open-wire +protocol. You will find the queue example for open wire, and the chat example. The virtual-topic-mapping examples shows how to map the ActiveMQ 5.x Virtual Topic naming convention to work with the Artemis Address model. -Paging ------- +## Paging -The `paging` example shows how Apache ActiveMQ Artemis can support huge queues even -when the server is running in limited RAM. It does this by transparently +The `paging` example shows how Apache ActiveMQ Artemis can support huge queues +even when the server is running in limited RAM. It does this by transparently *paging* messages to disk, and *depaging* them when they are required. -Pre-Acknowledge ---------------- +## Pre-Acknowledge -Standard JMS supports three acknowledgement modes:` - AUTO_ACKNOWLEDGE`, `CLIENT_ACKNOWLEDGE`, and -`DUPS_OK_ACKNOWLEDGE`. For a full description on these modes please -consult the JMS specification, or any JMS tutorial. +Standard JMS supports three acknowledgement modes:` AUTO_ACKNOWLEDGE`, +`CLIENT_ACKNOWLEDGE`, and `DUPS_OK_ACKNOWLEDGE`. For a full description on +these modes please consult the JMS specification, or any JMS tutorial. -All of these standard modes involve sending acknowledgements from the -client to the server. However in some cases, you really don't mind -losing messages in event of failure, so it would make sense to -acknowledge the message on the server before delivering it to the -client. This example demonstrates how Apache ActiveMQ Artemis allows this with an extra -acknowledgement mode. +All of these standard modes involve sending acknowledgements from the client to +the server. However in some cases, you really don't mind losing messages in +event of failure, so it would make sense to acknowledge the message on the +server before delivering it to the client. This example demonstrates how Apache +ActiveMQ Artemis allows this with an extra acknowledgement mode. -Message Producer Rate Limiting ------------------------------- +## Message Producer Rate Limiting -The `producer-rte-limit` example demonstrates how, with Apache ActiveMQ Artemis, you -can specify a maximum send rate at which a JMS message producer will -send messages. +The `producer-rte-limit` example demonstrates how, with Apache ActiveMQ +Artemis, you can specify a maximum send rate at which a JMS message producer +will send messages. -Queue ------ +## Queue A simple example demonstrating a queue. -Message Redistribution ----------------------- +## Message Redistribution -The `queue-message-redistribution` example demonstrates message -redistribution between queues with the same name deployed in different -nodes of a cluster. +The `queue-message-redistribution` example demonstrates message redistribution +between queues with the same name deployed in different nodes of a cluster. -Queue Requestor ---------------- +## Queue Requestor A simple example demonstrating a JMS queue requestor. -Queue with Message Selector ---------------------------- +## Queue with Message Selector -The `queue-selector` example shows you how to selectively consume -messages using message selectors with queue consumers. +The `queue-selector` example shows you how to selectively consume messages +using message selectors with queue consumers. -Reattach Node example ---------------------- +## Reattach Node example -The `Reattach Node` example shows how a client can try to reconnect to -the same server instead of failing the connection immediately and -notifying any user ExceptionListener objects. Apache ActiveMQ Artemis can be configured -to automatically retry the connection, and reattach to the server when -it becomes available again across the network. +The `Reattach Node` example shows how a client can try to reconnect to the same +server instead of failing the connection immediately and notifying any user +ExceptionListener objects. Apache ActiveMQ Artemis can be configured to +automatically retry the connection, and reattach to the server when it becomes +available again across the network. -Replicated Failback example ---------------------------- +## Replicated Failback example -An example showing how failback works when using replication, In this -example a live server will replicate all its Journal to a backup server -as it updates it. When the live server crashes the backup takes over -from the live server and the client reconnects and carries on from where -it left off. +An example showing how failback works when using replication, In this example a +live server will replicate all its Journal to a backup server as it updates it. +When the live server crashes the backup takes over from the live server and the +client reconnects and carries on from where it left off. -Replicated Failback static example ----------------------------------- +## Replicated Failback static example -An example showing how failback works when using replication, but this -time with static connectors +An example showing how failback works when using replication, but this time +with static connectors -Replicated multiple failover example ------------------------------------- +## Replicated multiple failover example -An example showing how to configure multiple backups when using -replication +An example showing how to configure multiple backups when using replication -Replicated Failover transaction example ---------------------------------------- +## Replicated Failover transaction example -An example showing how failover works with a transaction when using -replication +An example showing how failover works with a transaction when using replication -Request-Reply example ---------------------- +## Request-Reply example A simple example showing the JMS request-response pattern. -Scheduled Message ------------------ +## Scheduled Message -The `scheduled-message` example shows you how to send a scheduled -message to an address/queue with Apache ActiveMQ Artemis. Scheduled messages won't get +The `scheduled-message` example shows you how to send a scheduled message to an +address/queue with Apache ActiveMQ Artemis. Scheduled messages won't get delivered until a specified time in the future. -Security --------- +## Security The `security` example shows you how configure and use role based queue security with Apache ActiveMQ Artemis. -Send Acknowledgements ---------------------- +## Send Acknowledgements -The `send-acknowledgements` example shows you how to use Apache ActiveMQ Artemis's -advanced *asynchronous send acknowledgements* feature to obtain -acknowledgement from the server that sends have been received and -processed in a separate stream to the sent messages. +The `send-acknowledgements` example shows you how to use Apache ActiveMQ +Artemis's advanced *asynchronous send acknowledgements* feature to obtain +acknowledgement from the server that sends have been received and processed in +a separate stream to the sent messages. -Slow Consumer -------------- +## Slow Consumer -The `slow-consumer` example shows you how to detect slow consumers and configure -a slow consumer policy in Apache ActiveMQ Artemis's +The `slow-consumer` example shows you how to detect slow consumers and +configure a slow consumer policy in Apache ActiveMQ Artemis's -Spring Integration ------------------- +## Spring Integration -This example shows how to use embedded JMS using Apache ActiveMQ Artemis's Spring -integration. +This example shows how to use embedded JMS using Apache ActiveMQ Artemis's +Spring integration. -SSL Transport -------------- +## SSL Transport -The `ssl-enabled` shows you how to configure SSL with Apache ActiveMQ Artemis to send -and receive message. +The `ssl-enabled` shows you how to configure SSL with Apache ActiveMQ Artemis +to send and receive message. -Static Message Selector ------------------------ +## Static Message Selector -The `static-selector` example shows you how to configure an Apache ActiveMQ Artemis core -queue with static message selectors (filters). +The `static-selector` example shows you how to configure an Apache ActiveMQ +Artemis core queue with static message selectors (filters). -Static Message Selector Using JMS ---------------------------------- +## Static Message Selector Using JMS -The `static-selector-jms` example shows you how to configure an Apache ActiveMQ Artemis -queue with static message selectors (filters) using JMS. +The `static-selector-jms` example shows you how to configure an Apache ActiveMQ +Artemis queue with static message selectors (filters) using JMS. -Stomp ------ +## Stomp -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages. -Stomp1.1 --------- +## Stomp1.1 -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages via a Stomp 1.1 connection. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages via a Stomp 1.1 connection. -Stomp1.2 --------- +## Stomp1.2 -The `stomp` example shows you how to configure an Apache ActiveMQ Artemis server to send -and receive Stomp messages via a Stomp 1.2 connection. +The `stomp` example shows you how to configure an Apache ActiveMQ Artemis +server to send and receive Stomp messages via a Stomp 1.2 connection. -Stomp Over Web Sockets ----------------------- +## Stomp Over Web Sockets -The `stomp-websockets` example shows you how to configure an Apache ActiveMQ Artemis -server to send and receive Stomp messages directly from Web browsers +The `stomp-websockets` example shows you how to configure an Apache ActiveMQ +Artemis server to send and receive Stomp messages directly from Web browsers (provided they support Web Sockets). -Symmetric Cluster ------------------ +## Symmetric Cluster -The `symmetric-cluster` example demonstrates a symmetric cluster set-up -with Apache ActiveMQ Artemis. +The `symmetric-cluster` example demonstrates a symmetric cluster set-up with +Apache ActiveMQ Artemis. -Apache ActiveMQ Artemis has extremely flexible clustering which allows you to set-up -servers in many different topologies. The most common topology that +Apache ActiveMQ Artemis has extremely flexible clustering which allows you to +set-up servers in many different topologies. The most common topology that you'll perhaps be familiar with if you are used to application server clustering is a symmetric cluster. With a symmetric cluster, the cluster is homogeneous, i.e. each node is -configured the same as every other node, and every node is connected to -every other node in the cluster. +configured the same as every other node, and every node is connected to every +other node in the cluster. -Temporary Queue ---------------- +## Temporary Queue A simple example demonstrating how to use a JMS temporary queue. -Topic ------ +## Topic A simple example demonstrating a JMS topic. -Topic Hierarchy ---------------- +## Topic Hierarchy -Apache ActiveMQ Artemis supports topic hierarchies. With a topic hierarchy you can -register a subscriber with a wild-card and that subscriber will receive -any messages sent to an address that matches the wild card. +Apache ActiveMQ Artemis supports topic hierarchies. With a topic hierarchy you +can register a subscriber with a wild-card and that subscriber will receive any +messages sent to an address that matches the wild card. -Topic Selector 1 ----------------- +## Topic Selector 1 -The `topic-selector-example1` example shows you how to send message to a -JMS Topic, and subscribe them using selectors with Apache ActiveMQ Artemis. +The `topic-selector-example1` example shows you how to send message to a JMS +Topic, and subscribe them using selectors with Apache ActiveMQ Artemis. -Topic Selector 2 ----------------- +## Topic Selector 2 -The `topic-selector-example2` example shows you how to selectively -consume messages using message selectors with topic consumers. +The `topic-selector-example2` example shows you how to selectively consume +messages using message selectors with topic consumers. -Transaction Failover --------------------- +## Transaction Failover The `transaction-failover` example demonstrates two servers coupled as a -live-backup pair for high availability (HA), and a client using a -transacted JMS session failing over from live to backup when the live -server is crashed. +live-backup pair for high availability (HA), and a client using a transacted +JMS session failing over from live to backup when the live server is crashed. -Apache ActiveMQ Artemis implements failover of client connections between live and -backup servers. This is implemented by the sharing of a journal between -the servers. When a live node crashes, the client connections can carry -and continue to send and consume messages. When transacted sessions are -used, once and only once message delivery is guaranteed. +Apache ActiveMQ Artemis implements failover of client connections between live +and backup servers. This is implemented by the sharing of a journal between the +servers. When a live node crashes, the client connections can carry and +continue to send and consume messages. When transacted sessions are used, once +and only once message delivery is guaranteed. -Failover Without Transactions ------------------------------ +## Failover Without Transactions -The `stop-server-failover` example demonstrates failover of the JMS -connection from one node to another when the live server crashes using a -JMS non-transacted session. +The `stop-server-failover` example demonstrates failover of the JMS connection +from one node to another when the live server crashes using a JMS +non-transacted session. -Transactional Session ---------------------- +## Transactional Session -The `transactional` example shows you how to use a transactional Session -with Apache ActiveMQ Artemis. +The `transactional` example shows you how to use a transactional Session with +Apache ActiveMQ Artemis. -XA Heuristic ------------- +## XA Heuristic -The `xa-heuristic` example shows you how to make an XA heuristic -decision through Apache ActiveMQ Artemis Management Interface. A heuristic decision is -a unilateral decision to commit or rollback an XA transaction branch -after it has been prepared. +The `xa-heuristic` example shows you how to make an XA heuristic decision +through Apache ActiveMQ Artemis Management Interface. A heuristic decision is a +unilateral decision to commit or rollback an XA transaction branch after it has +been prepared. -XA Receive ----------- +## XA Receive -The `xa-receive` example shows you how message receiving behaves in an -XA transaction in Apache ActiveMQ Artemis. +The `xa-receive` example shows you how message receiving behaves in an XA +transaction in Apache ActiveMQ Artemis. -XA Send -------- +## XA Send The `xa-send` example shows you how message sending behaves in an XA transaction in Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/exclusive-queues.md b/docs/user-manual/en/exclusive-queues.md index 3dc751c3ad5..7e086ea174f 100644 --- a/docs/user-manual/en/exclusive-queues.md +++ b/docs/user-manual/en/exclusive-queues.md @@ -3,44 +3,43 @@ Exclusive queues are special queues which route all messages to only one consumer at a time. -This is useful when you want all messages to be processed serially by the same consumer, -when a producer does not specify [Message Grouping](message-grouping.md). +This is useful when you want all messages to be processed serially by the same +consumer, when a producer does not specify [Message Grouping](message-grouping.md). An example might be orders sent to an address and you need to consume them in the exact same order they were produced. -Obviously exclusive queues have a draw back that you cannot scale out the consumers to -improve consumption as only one consumer would technically be active. +Obviously exclusive queues have a draw back that you cannot scale out the +consumers to improve consumption as only one consumer would technically be active. Here we advise that you look at message groups first. ## Configuring Exclusive Queues -Exclusive queues can be statically configured using the `exclusive` boolean property: +Exclusive queues can be statically configured using the `exclusive` boolean +property: ```xml - - - ... -
- - - -
-
-
+
+ + + +
``` -Specified on creating a Queue by using the CORE api specifying the parameter `exclusive` to `true`. +Specified on creating a Queue by using the CORE api specifying the parameter +`exclusive` to `true`. -Or on auto-create when using the JMS Client by using address parameters when creating the destination used by the consumer. +Or on auto-create when using the JMS Client by using address parameters when +creating the destination used by the consumer. ```java Queue queue = session.createQueue("my.destination.name?exclusive=true"); Topic topic = session.createTopic("my.destination.name?exclusive=true"); ``` -Also the default for all queues under and address can be defaulted using the address-setting configuration: +Also the default for all queues under and address can be defaulted using the +`address-setting` configuration: ```xml @@ -48,12 +47,12 @@ Also the default for all queues under and address can be defaulted using the add ``` -By default, `default-exclusive-queue` is `false`. Address wildcards can be used -to configure exclusive queues for a set of addresses (see [here](wildcard-syntax.md)). +By default, `default-exclusive-queue` is `false`. Address +[wildcards](wildcard-syntax.md) can be used to configure exclusive queues for a +set of addresses. ## Example -See `Exclusive Queue` in [examples](examples.md). - -For additional examples see `org.apache.activemq.artemis.tests.integration.jms.client.ExclusiveTest` +See the [exclusive queue example](examples.md#exclusive) which shows how +exclusive queues are configured and used with JMS. diff --git a/docs/user-manual/en/filter-expressions.md b/docs/user-manual/en/filter-expressions.md index 0c9183c9d69..425b5c1b639 100644 --- a/docs/user-manual/en/filter-expressions.md +++ b/docs/user-manual/en/filter-expressions.md @@ -10,19 +10,19 @@ please the JMS javadoc for Filter expressions are used in several places in Apache ActiveMQ Artemis -- Predefined Queues. When pre-defining a queue, in - `broker.xml` in either the core or jms configuration a filter - expression can be defined for a queue. Only messages that match the - filter expression will enter the queue. +- Predefined Queues. When pre-defining a queue, in + `broker.xml` in either the core or jms configuration a filter + expression can be defined for a queue. Only messages that match the + filter expression will enter the queue. -- Core bridges can be defined with an optional filter expression, only - matching messages will be bridged (see [Core Bridges](core-bridges.md)). +- Core bridges can be defined with an optional filter expression, only + matching messages will be bridged (see [Core Bridges](core-bridges.md)). -- Diverts can be defined with an optional filter expression, only - matching messages will be diverted (see [Diverts](diverts.md)). +- Diverts can be defined with an optional filter expression, only + matching messages will be diverted (see [Diverts](diverts.md)). -- Filter are also used programmatically when creating consumers, - queues and in several places as described in [management](management.md). +- Filter are also used programmatically when creating consumers, + queues and in several places as described in [management](management.md). There are some differences between JMS selector expressions and Apache ActiveMQ Artemis core filter expressions. Whereas JMS selector expressions operate on a @@ -31,21 +31,21 @@ JMS message, Apache ActiveMQ Artemis core filter expressions operate on a core m The following identifiers can be used in a core filter expressions to refer to attributes of the core message in an expression: -- `AMQPriority`. To refer to the priority of a message. Message - priorities are integers with valid values from `0 - 9`. `0` is the - lowest priority and `9` is the highest. E.g. - `AMQPriority = 3 AND animal = 'aardvark'` +- `AMQPriority`. To refer to the priority of a message. Message + priorities are integers with valid values from `0 - 9`. `0` is the + lowest priority and `9` is the highest. E.g. + `AMQPriority = 3 AND animal = 'aardvark'` -- `AMQExpiration`. To refer to the expiration time of a message. The - value is a long integer. +- `AMQExpiration`. To refer to the expiration time of a message. The + value is a long integer. -- `AMQDurable`. To refer to whether a message is durable or not. The - value is a string with valid values: `DURABLE` or `NON_DURABLE`. +- `AMQDurable`. To refer to whether a message is durable or not. The + value is a string with valid values: `DURABLE` or `NON_DURABLE`. -- `AMQTimestamp`. The timestamp of when the message was created. The - value is a long integer. +- `AMQTimestamp`. The timestamp of when the message was created. The + value is a long integer. -- `AMQSize`. The size of a message in bytes. The value is an integer. +- `AMQSize`. The size of a message in bytes. The value is an integer. Any other identifiers used in core filter expressions will be assumed to be properties of the message. diff --git a/docs/user-manual/en/flow-control.md b/docs/user-manual/en/flow-control.md index 86654f2b078..44fbee3f166 100644 --- a/docs/user-manual/en/flow-control.md +++ b/docs/user-manual/en/flow-control.md @@ -37,11 +37,11 @@ bytes). The value can be: -- `-1` for an *unbounded* buffer +- `-1` for an *unbounded* buffer -- `0` to not buffer any messages. +- `0` to not buffer any messages. -- `>0` for a buffer with the given maximum size in bytes. +- `>0` for a buffer with the given maximum size in bytes. Setting the consumer window size can considerably improve performance depending on the messaging use case. As an example, let's consider the @@ -106,7 +106,7 @@ control. The default value is `-1`. Please see [the examples chapter](examples.md) for a working example of limiting consumer rate. -> **Note** +> **Note:** > > Rate limited flow control can be used in conjunction with window based > flow control. Rate limited flow control only effects how many messages @@ -198,7 +198,7 @@ to prevent that max size being exceeded. Note the policy must be set to `BLOCK` to enable blocking producer flow control. -> **Note** +> **Note:** > > Note that in the default configuration all addresses are set to block > producers after 10 MiB of message data is in the address. This means @@ -207,7 +207,7 @@ control. > want this behaviour increase the `max-size-bytes` parameter or change > the address full message policy. -> **Note** +> **Note:** > > Producer credits are allocated from the broker to the client. Flow control > credit checking (i.e. checking a producer has enough credit) is done on the diff --git a/docs/user-manual/en/graceful-shutdown.md b/docs/user-manual/en/graceful-shutdown.md index 6c5cba3d007..3869b13c91b 100644 --- a/docs/user-manual/en/graceful-shutdown.md +++ b/docs/user-manual/en/graceful-shutdown.md @@ -1,21 +1,19 @@ # Graceful Server Shutdown -In certain circumstances an administrator might not want to disconnect -all clients immediately when stopping the broker. In this situation the -broker can be configured to shutdown *gracefully* using the -`graceful-shutdown-enabled` boolean configuration parameter. +In certain circumstances an administrator might not want to disconnect all +clients immediately when stopping the broker. In this situation the broker can +be configured to shutdown *gracefully* using the `graceful-shutdown-enabled` +boolean configuration parameter. -When the `graceful-shutdown-enabled` configuration parameter is `true` -and the broker is shutdown it will first prevent any additional clients -from connecting and then it will wait for any existing connections to -be terminated by the client before completing the shutdown process. The -default value is `false`. +When the `graceful-shutdown-enabled` configuration parameter is `true` and the +broker is shutdown it will first prevent any additional clients from connecting +and then it will wait for any existing connections to be terminated by the +client before completing the shutdown process. The default value is `false`. Of course, it's possible a client could keep a connection to the broker -indefinitely effectively preventing the broker from shutting down -gracefully. To deal with this of situation the -`graceful-shutdown-timeout` configuration parameter is available. This -tells the broker (in milliseconds) how long to wait for all clients to -disconnect before forcefully disconnecting the clients and proceeding -with the shutdown process. The default value is `-1` which means the -broker will wait indefinitely for clients to disconnect. +indefinitely effectively preventing the broker from shutting down gracefully. +To deal with this of situation the `graceful-shutdown-timeout` configuration +parameter is available. This tells the broker (in milliseconds) how long to +wait for all clients to disconnect before forcefully disconnecting the clients +and proceeding with the shutdown process. The default value is `-1` which means +the broker will wait indefinitely for clients to disconnect. diff --git a/docs/user-manual/en/ha.md b/docs/user-manual/en/ha.md index 2fc0585e480..e78b1e4bb9c 100644 --- a/docs/user-manual/en/ha.md +++ b/docs/user-manual/en/ha.md @@ -52,14 +52,14 @@ This of course means there will be no Backup Strategy and is the default if none is provided, however this is used to configure `scale-down` which we will cover in a later chapter. -> **Note** +> **Note:** > > The `ha-policy` configurations replaces any current HA configuration > in the root of the `broker.xml` configuration. All old > configuration is now deprecated although best efforts will be made to > honour it if configured this way. -> **Note** +> **Note:** > > Only persistent message data will survive failover. Any non persistent > message data will not be available after failover. @@ -115,7 +115,7 @@ synchronizing the data with its live server. The time it will take for this to happen will depend on the amount of data to be synchronized and the connection speed. -> **Note** +> **Note:** > > In general, synchronization occurs in parallel with current network traffic so > this won't cause any blocking on current clients. However, there is a critical @@ -137,37 +137,37 @@ Cluster Connection also defines how backup servers will find the remote live servers to pair with. Refer to [Clusters](clusters.md) for details on how this is done, and how to configure a cluster connection. Notice that: -- Both live and backup servers must be part of the same cluster. - Notice that even a simple live/backup replicating pair will require - a cluster configuration. +- Both live and backup servers must be part of the same cluster. + Notice that even a simple live/backup replicating pair will require + a cluster configuration. -- Their cluster user and password must match. +- Their cluster user and password must match. Within a cluster, there are two ways that a backup server will locate a live server to replicate from, these are: -- `specifying a node group`. You can specify a group of live servers - that a backup server can connect to. This is done by configuring - `group-name` in either the `master` or the `slave` element of the - `broker.xml`. A Backup server will only connect to a - live server that shares the same node group name +- `specifying a node group`. You can specify a group of live servers + that a backup server can connect to. This is done by configuring + `group-name` in either the `master` or the `slave` element of the + `broker.xml`. A Backup server will only connect to a + live server that shares the same node group name -- `connecting to any live`. This will be the behaviour if `group-name` - is not configured allowing a backup server to connect to any live - server +- `connecting to any live`. This will be the behaviour if `group-name` + is not configured allowing a backup server to connect to any live + server -> **Note** +> **Note:** > > A `group-name` example: suppose you have 5 live servers and 6 backup > servers: > -> - `live1`, `live2`, `live3`: with `group-name=fish` +> - `live1`, `live2`, `live3`: with `group-name=fish` > -> - `live4`, `live5`: with `group-name=bird` +> - `live4`, `live5`: with `group-name=bird` > -> - `backup1`, `backup2`, `backup3`, `backup4`: with `group-name=fish` +> - `backup1`, `backup2`, `backup3`, `backup4`: with `group-name=fish` > -> - `backup5`, `backup6`: with `group-name=bird` +> - `backup5`, `backup6`: with `group-name=bird` > > After joining the cluster the backups with `group-name=fish` will > search for live servers with `group-name=fish` to pair with. Since @@ -183,7 +183,7 @@ until it finds a live server that has no current backup configured. If no live server is available it will wait until the cluster topology changes and repeats the process. -> **Note** +> **Note:** > > This is an important distinction from a shared-store backup, if a > backup starts and does not find a live server, the server will just @@ -240,101 +240,44 @@ The backup server must be similarly configured but as a `slave` The following table lists all the `ha-policy` configuration elements for HA strategy Replication for `master`: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`check-for-live-server`Whether to check the cluster for a (live) server using our own server ID - when starting up. This option is only necessary for performing 'fail-back' - on replicating servers.
`cluster-name`Name of the cluster configuration to use for replication. This setting is - only necessary if you configure multiple cluster connections. If configured then - the connector configuration of the cluster configuration with this name will be - used when connecting to the cluster to discover if a live server is already running, - see `check-for-live-server`. If unset then the default cluster connections configuration - is used (the first one configured).
`group-name`If set, backup servers will only pair with live servers with matching group-name.
`initial-replication-sync-timeout`The amount of time the replicating server will wait at the completion of the initial - replication process for the replica to acknowledge it has received all the necessary - data. The default is 30,000 milliseconds. Note: during this interval any - journal related operations will be blocked.
+- `check-for-live-server` + + Whether to check the cluster for a (live) server using our own server ID when starting up. This option is only necessary for performing 'fail-back' on replicating servers. + +- `cluster-name` + + Name of the cluster configuration to use for replication. This setting is only necessary if you configure multiple cluster connections. If configured then the connector configuration of the cluster configuration with this name will be used when connecting to the cluster to discover if a live server is already running, see `check-for-live-server`. If unset then the default cluster connections configuration is used (the first one configured). + +- `group-name` + + If set, backup servers will only pair with live servers with matching group-name. + +- `initial-replication-sync-timeout` + + The amount of time the replicating server will wait at the completion of the initial replication process for the replica to acknowledge it has received all the necessary data. The default is 30,000 milliseconds. **Note:** during this interval any journal related operations will be blocked. The following table lists all the `ha-policy` configuration elements for HA strategy Replication for `slave`: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`cluster-name`Name of the cluster configuration to use for replication. - This setting is only necessary if you configure multiple cluster - connections. If configured then the connector configuration of - the cluster configuration with this name will be used when - connecting to the cluster to discover if a live server is already - running, see `check-for-live-server`. If unset then the default - cluster connections configuration is used (the first one configured)
`group-name`If set, backup servers will only pair with live servers with matching group-name
`max-saved-replicated-journals-size`This specifies how many times a replicated backup server - can restart after moving its files on start. Once there are - this number of backup journal files the server will stop permanently - after if fails back.
`allow-failback`Whether a server will automatically stop when a another places a - request to take over its place. The use case is when the backup has - failed over
`initial-replication-sync-timeout`After failover and the slave has become live, this is - set on the new live server. It represents the amount of time - the replicating server will wait at the completion of the - initial replication process for the replica to acknowledge - it has received all the necessary data. The default is - 30,000 milliseconds. Note: during this interval any - journal related operations will be blocked.
+- `cluster-name` + + Name of the cluster configuration to use for replication. This setting is only necessary if you configure multiple cluster connections. If configured then the connector configuration of the cluster configuration with this name will be used when connecting to the cluster to discover if a live server is already running, see `check-for-live-server`. If unset then the default cluster connections configuration is used (the first one configured) + +- `group-name` + + If set, backup servers will only pair with live servers with matching group-name + +- `max-saved-replicated-journals-size` + + This specifies how many times a replicated backup server can restart after moving its files on start. Once there are this number of backup journal files the server will stop permanently after if fails back. + +- `allow-failback` + + Whether a server will automatically stop when a another places a request to take over its place. The use case is when the backup has failed over + +- `initial-replication-sync-timeout` + + After failover and the slave has become live, this is set on the new live server. It represents the amount of time the replicating server will wait at the completion of the initial replication process for the replica to acknowledge it has received all the necessary data. The default is 30,000 milliseconds. **Note:** during this interval any journal related operations will be blocked. ### Shared Store @@ -402,7 +345,7 @@ In order for live - backup groups to operate properly with a shared store, both servers must have configured the location of journal directory to point to the *same shared location* (as explained in [Configuring the message journal](persistence.md)) -> **Note** +> **Note:** > > todo write something about GFS @@ -504,67 +447,24 @@ automatically by setting the following property in the The following table lists all the `ha-policy` configuration elements for HA strategy shared store for `master`: - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`failover-on-server-shutdown`If set to true then when this server is stopped - normally the backup will become live assuming failover. - If false then the backup server will remain passive. - Note that if false you want failover to occur the you - can use the the management API as explained at [Management](management.md)
`wait-for-activation`If set to true then server startup will wait until it is activated. - If set to false then server startup will be done in the background. - Default is true.
+- `failover-on-server-shutdown` + + If set to true then when this server is stopped normally the backup will become live assuming failover. If false then the backup server will remain passive. Note that if false you want failover to occur the you can use the the management API as explained at [Management](management.md). + +- `wait-for-activation` + + If set to true then server startup will wait until it is activated. If set to false then server startup will be done in the background. Default is true. The following table lists all the `ha-policy` configuration elements for HA strategy Shared Store for `slave`: - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`failover-on-server-shutdown`In the case of a backup that has become live. then - when set to true then when this server is stopped normally - the backup will become liveassuming failover. If false then - the backup server will remain passive. Note that if false - you want failover to occur the you can use the the management - API as explained at [Management](management.md)
`allow-failback`Whether a server will automatically stop when a another - places a request to take over its place. The use case is - when the backup has failed over.
+- `failover-on-server-shutdown` + + In the case of a backup that has become live. then when set to true then when this server is stopped normally the backup will become liveassuming failover. If false then the backup server will remain passive. Note that if false you want failover to occur the you can use the the management API as explained at [Management](management.md). + +- `allow-failback` + + Whether a server will automatically stop when a another places a request to take over its place. The use case is when the backup has failed over. #### Colocated Backup Servers @@ -613,7 +513,7 @@ say 100 (which is the default) and a connector is using port 61616 then this will be set to 5545 for the first server created, 5645 for the second and so on. -> **Note** +> **Note:** > > for INVM connectors and Acceptors the id will have > `colocated_backup_n` appended, where n is the backup server number. @@ -648,40 +548,25 @@ creating server but have the new backups name appended. The following table lists all the `ha-policy` configuration elements for colocated policy: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
`request-backup`If true then the server will request a backup on another node
`backup-request-retries`How many times the live server will try to request a backup, -1 means for ever.
`backup-request-retry-interval`How long to wait for retries between attempts to request a backup server.
`max-backups`How many backups a live server can create
`backup-port-offset`The offset to use for the Connectors and Acceptors when creating a new backup server.
+- `request-backup` + + If true then the server will request a backup on another node + +- `backup-request-retries` + + How many times the live server will try to request a backup, -1 means for ever. + +- `backup-request-retry-interval` + + How long to wait for retries between attempts to request a backup server. + +- `max-backups` + + How many backups a live server can create + +- `backup-port-offset` + + The offset to use for the Connectors and Acceptors when creating a new backup server. ### Scaling Down @@ -814,9 +699,9 @@ be high enough to deal with the time needed to scale down. Apache ActiveMQ Artemis defines two types of client failover: -- Automatic client failover +- Automatic client failover -- Application-level client failover +- Application-level client failover Apache ActiveMQ Artemis also provides 100% transparent automatic reattachment of connections to the same server (e.g. in case of transient network @@ -970,7 +855,7 @@ response will come back. In this case it is not easy for the client to determine whether the transaction commit was actually processed on the live server before failure occurred. -> **Note** +> **Note:** > > If XA is being used either via JMS or through the core API then an > `XAException.XA_RETRY` is thrown. This is to inform Transaction @@ -988,7 +873,7 @@ retried, duplicate detection will ensure that any durable messages resent in the transaction will be ignored on the server to prevent them getting sent more than once. -> **Note** +> **Note:** > > By catching the rollback exceptions and retrying, catching unblocked > calls and enabling duplicate detection, once and only once delivery @@ -1025,28 +910,13 @@ following: JMSException error codes - - - - - - - - - - - - - - - - - - - - - -
Error codeDescription
FAILOVERFailover has occurred and we have successfully reattached or reconnected.
DISCONNECTNo failover has occurred and we are disconnected.
+- `FAILOVER` + + Failover has occurred and we have successfully reattached or reconnected. + +- `DISCONNECT` + + No failover has occurred and we are disconnected. ### Application-Level Failover @@ -1063,7 +933,7 @@ connection failure is detected. In your `ExceptionListener`, you would close your old JMS connections, potentially look up new connection factory instances from JNDI and creating new connections. -For a working example of application-level failover, please see [the examples](examples.md) chapter. +For a working example of application-level failover, please see [the Application-Layer Failover Example](examples.md#application-layer-failover). If you are using the core API, then the procedure is very similar: you would set a `FailureListener` on the core `ClientSession` instances. diff --git a/docs/user-manual/en/images/architecture1.jpg b/docs/user-manual/en/images/architecture1.jpg index d2b9de476269b64c7114bc07f9c9acb404133ecb..170dd5c0a02d2fcee3ff103c8cea775c7359b49d 100644 GIT binary patch literal 76604 zcmd431wd6x*FU^LNA@CXPGkWe2!Kte%6KzNAx5Csh#0|NsQ8S4=y`Xf|y4D_2I z5cj}7P|)|Gq3@$VKzM-uKTg+8016zq3e5K)Pyk32hTv>9`9u)L8VUfLUgx-o88$d}3yv(fu-U=~Ji zJfWe)YH}d{<1W*t?gp_qRTy^7GV=wJ{uFUmiCOgiH+FB~9x}7Qmb4LmVXTlbZ>r3TAS_`m{oYgd9=A7 z+Gsq%Rr7l6%FafGD$fqyHQ*pY?Yw#5`-o~h=S&Qj&D&_v}e%oT+VC^ zJ}JR~xgGz{_A62Pc`hopj!agkXO1Zx4Q`u38E4mkS<>&P0r}doYJt18CFJ+sjKjFI}E8&b*0GJ9PQb zz}g+Q@Lk}h;mdzCJTn<=%wB)Ug5AczSfm3{bq&YMihw{zM*m) zVB1SnT_yL#2q1?Uh{+Yuu^e4|rZ%^>g}K4OWSI?55f-l)y5}w~kTW&}kL+yp!ZYwt zN1wm#e?C*cCZYbUzP^+HTxQ+1Nn-J5=`F7vPjy`VnqJ>P#E+DZ0r`xxMdMJ3$=^eYUc7I5ZF%suwgb;H;~AR7sNI8vWsuYwl4++RB8j zkqBJrjok;X_~Y%VcnUCk55GsIqmO69_eC#VWs()M_KT<0CZ5_cE#F695-0HhiXH8FD^7?#o_H#t;?f@ zjfqby1}@!S5Ax5?96sMz{76J_x$ZYCB*a@icsgBDtY=9#BA|_MJ0wB)5`GnH!_a1+ zzD4iI!(Ya@JB0hTfqyZ%VIh2n)dO+s3X}eXQzh(MRhv}(3B47GWS!X3&-nhVvbL`= zkEN~sGkhZlNP*Vl*1(g;9}$qCnAIe@8xQk^6GLMHB8y=61l7#6Ll{QPTR$bS#v4b?GJEy5w&k^7#?jy7-9rTr z+9|FvDW2zjsM1$+?}?_%qJ$90Y+igdA{BT7Eci2S9%}=rynXA;x}G zCs4xT3*kp?Q0hS@Hvm9emVK!rs$9~LasS=(7Y2R55n$_ohnx}6sCx(FabQQETi>(| zEZV%xI_d$!Y49%&zp!k*&T7IxWVohJrrcm2!Xq;s>mTd^$V}zNANJXiNjDsn1H7{P z4f8kYXII4F9lh0KFxVgejo}T%hU|%)_5j>-ZW6@)ZWeOIEqf2fi%Q_P{&$P~LY#?R ze3iNH0A{6Bz0Z@36K{y#U;{_YgjrZueed3`sQpCNN}DAJuz&DJyFlG{g20{1dEiPOueQ}74||Hfmp zbH~=4T0u zJ~=Cn(;V>*_=(LI}MN)-Kk@TmtTCIB=E< zhOE6grxpp#ipafG=y|1+=qS&$1&Yxj_kZ&;HDA%O++@#WlB1oB(-4m$f+d`S@Qh#oN9z#{+xz5{r&TifP)( z!85=;MSIR!T%`UKd07BW-f_l&R-+rNM_ucWITOW=PZV+TO}G?(_SkRWDFcXY{&M_$$MzCi#QoPEtCzuPunKatbs@ZU2Y`>Roj=BxxTfw_ zGF{LePP$XPtJGz(?bf`v^wJ#w2oAQQCo`!40ClJ~QD&@j=)i~qpeM~duy@7{LX>j{ zAQZpRegG-inErNgAh1&=##jgLS_PI1$845=*mX1qbM>|Wh{^fQ8bd77Y-S~J^v5rr zfKN&2Kw+SW0-)1E*sDwhAVXrKIYaJh0|;au*@*vWVK`(gvnc+Y^VnP!&094J#DZC= zVFjI>NHID%7&t*Mn(B|CtCc9zhE0v= zOhQOMOBG4=dtC^8GzcjFk__5`PnedQnc)WZjQcz?Tw6DmKybR+($;f{PgauW+gh~} z!w@&v%1t@@)HQb&qH3swr1^{|X)6=~6o!f&+x~?g^Tka&jIa(8qPDkqzb40ZaCyw9 z*$a@@(@f-Hb#56KZN9s&Gym!tdeF;y|71FScYemUNzDOUdziY-#T}k>A<4R%dTmMF;AE?7*g5N|b@h39c#nw_NR-K%MYi?mzj3|-67ylaUcl_D^D0#Z^MDM{ARnhm06H+ifzkl(c!JrUTv z3JL$luiS)x&nY(7Nt2o=-j9;4+*-_(eRnCtOoxh%c|tV|hko1z;FEc2R{j9Xz};kK zM(7%FYwmT2cf(VYNi-Gj;w7*Ftu>RI$1^T87~ts*S(DU_34%^Ma~q zqa*{dO7CU#gxKz;l5-1W-27(jfOEof4(>R3B`@D(8Plm3q!#~NubX_at;-Bto@1rRaCRfvCEp#6Brf%Np_ zjdwE;^d2n`x`3AP;=u{GNxzNldW|XMaIIQcHBApV21Q!tUnBj7z&wxvZN#m%5T)Ss^`Y)} zS*qtMCX*<*w6nYu-z^v4Un*?`x9op<)`0L$-b#2m@(yagGWC;LCP~kCy|-wTJ-7kn zROZ9~t#|;11QCFMGLf-z2Tub=U~>%V8>fFth>yXw`D_dx;_rc{mj7I_n;6iq%iM2r z0QkA`yp#V(A;AXX*e)!(0Fd-B6#zCkRSL540RY@D{x-o7`czx?f&1NOvwCg-5MQlF zoO!tbo>jEVLP4ug0Me%IM)p57``uNXzYPr0-#URecyxw+ch+!yPs;70N|^q(PyqOK zHv?Lyb+S7Hhc@NIT_BbDKD(~I3F+^D?+axdWFR{$nD^)Ry1_a+M_^dnGl)tA8K#x6xRqF!aR$LcYjUWvaHgf$&J6Dnt9% zo7?=@dcpL-f{-XJtOiav4wr8%{wVEV6)KFJ) zEZ6ZTS+OqAv30$W{ddiS))^qGI2NSV*B!|T{XHQp_gMd^yJ>DJ`)4Z=by91{Jl5li z+^s{lzi$Yj#W>MG zbyf`jR(*T%{ml|0iEl+{6~7mFl(2C68cu zfe3wcG=@w9MK4{r)|d!Iz8r?srbPgS;-89#6w;UL_L^7MM+}xOZqL`_jPQyUh9>)1 zmnuf8c4RXg9ztBHs)Q<#m;N$j$RIX7h?Dh&&MZT(2gN^~N|Q(Ndl8N)-=%#lL%?qJU=iK*dKhCNzaoqn<7|NJXGIjYJNWG*4rcIP8QuPvdhv+ zT8s?)pZbQ^E6RCV9s8Q^x6nduRlHmOhhPA}L$4H$h8glqDMl!lMhC8pKsW;7VXgpg zE|2g#C9kA^^!xv*0uTIxk@+D2ezOVOgND0#nF#&|pn-c(5RfpKDCiil4^f%9(eUXQ z7@1gJW07dm6S5M$4R!~=dV~YNk%WMTyasldb`AJh3nNtS%l$VZU7Re1JuZ^xnf>2L zR*cefDXtLxf<>jAQ%vJ4{@8~9Bv7edo+5H_3zDN#tegvdYzSGBPwWUgxdw=Z({zsf zInTWla5WB;4ElF_FB9E9CZgf*(OLz$;jYh$IK{`E+O^IHEf?`s^9>M@mz(O(+O**~ zJm`Y|kohG2@w($h$;x(n1B)9M?qs>{>v-fx9+p085wA+0#((y&)A)uzNl6TP=0W}q z9bSQ^Ix1m{^~hynWj@7;3xmoWcOgZRJ+ZNTu3anasl-?B(RnYyCoP{keBANSIPfT> zYTx6-sPqd?9ozCOsx8#6(%F*nIpnwt(c(@B2!GVH)`n8kHBsEa?NK$NOV|P}bl219 zM7|J=H6ArRVXn9as05#7Il0&>?H>y;;ws^?zOF}_#9W{^(s|3zZep);4V*PlX?L73 z>s07N3x_B+S+*myLFL&uOuYe}m-M#^%rg=ry@!OMpnDR=AlOuWiQ%2BfMRkD$YUF~ zZ+w$0U4tUmn@(P_W$ZiF#pRzGZkq{a?<-2A>7+f1#0)S^Vcb0d@y8Gzw@=v6mW9f5 zvDZL9Y&mfp=Fj6({wE?ZVelA>=F~PMWQ2g}l$A!SJy$)EC?grVQ26y_*AsEQs5Sb#338jO%|5*?k?wfcnlI zBKHDUf^Il2==~dfzu4!Tr^QLF&bJq1=-wdTy9HA`9Zik^0&jhyl2&P}LkK|(tun>o zdsfE4vJdO@L__3-#9-xtrJUnXLAkVvuFfEi?gnZT)wCYco>@;vp7yvB-0A~S5~Of< zF`LcjUsrHHJkgBaMrs2cNzvuAwdeILGGoWvYi~4Tsbf~}w2pbx)Y99``4u3jfx)IR z^+(k^Wj{c}FGYQPKd_&Mh_W@&a?{bXOg4yXhzXS4?AQ^HTa_$Dvw5ghF?La02nzrZiq8-&@u zBH;Jt@<1`Mf7j}X{t4~ix|*bi&2aG)>tjpinHs9oOC%MHPFs|A8gnSa5!*GLN4}gd z6`lqTJ1w?`=ut8WIjP3Lw0%|u85dFFXs-wz-1XK5)Z@aZH>6TzX zco))K0~#R#;k2YdI-`3K{#96A1%7(=A|3_f+|J;3)jpg+@CwsuR%`iH8 zhOohHPd~rg2xMH5-GC9nKihJ|hlzF0s%H?Mq6ZC>5_gMpw z4P~D9kmerH-MMdQIRtG%x!{d9?c@Dk<@cu6BBt-D4YKI#3wncd%B7x+MY*dZ}y)uiWLDqx*lN|BAEC zUYdVzYsNaR8;`Lxpsw~W$m45ZwsyAMuwC~{5`P;#Ty&gk5Lx4Qz0lp6?p8I)y~ngs zj;bcz4IR4mYHJn+;F|^a@R(&leVkA-z(?;$AJOq;Gv^!Rs6gAy_Mi(NLxM9JrHNHn zzYk}eV;+Zy2F3W7ugI#SlBd7s)m&VwjRD9k}PE@i{#&7bqZPxtdD-J z{PW4XQudj(vcCX{7|C=sbnS22-~Zxp6X%cO)e2+aaE{Hci3Vobe<8+he0WTzbY9qF ztvS#zmh$z~CiBC)@7iB!B{9Ia2f_<;I4h5}=KT*zGf^4l?37+2S9@$vMhOF8I89Q> z#z7(-G9B-PfE#A>-Cb%LKkvK6&aHt0Zw1GAsMWWe-}DW06q7uA!S zpmN$!jZegJphO&~jqo0cdE!JhF;S&JX1rw1k2}fYoukq(Q02?889*}?;zz4Pt2KI5 zgE?UQd+a|jWcx5Ww)|?WEv4q0hS3Zhxk3l*l$TqmaizXoUxWTzv_>?s?=V*}s$2p{E68nkq1?`uQqVKs?!=xk=YuDjBr``1yR4t}P- zA(T6-560!?NJBs^)?)5R5%-N=NB(06B6Mn)hUA5REUcf3b7hf+lO>?lk`C~o!S0ptl6W?P6tfW7CBEbKd3ia8%tZ{##ww0Oko_{qfv$eeROYB~O}qgpn|WX_8aP zCajPi_r{B4>kl)Zf|fY9Xq%>5$Eb{Qi&YF#^~PHs;rEt^c_!$=KiBVJ##q&fx7a;> zv%sUbg!!SpXe}45an~0bcK&^IM;iB&@lTD6W=OC>a|^maVR-Z-FFN{yd-x1{k1=&| zpTkTR;10M<`SoJbE~-sv%+JC_&0G-9xVDdCEV-`HcDbVA(FHLcEV_{&OX^r|le5rB zJA_8R@l~BW#TDr3aha)!(AmP)mvv6+Hmv|DbKP%(c+Y1Ee7pvF=$PEKL5Mm}h=Sh5 zX(>LMAjSQFMTU$@gC=T`Gq;5NAl|3b$JBk<(t*V%M67wbqjo_1ihnxdiYiT-sEUeQ zfS|30AkkE@)wj(Bw8U1nyy*V@8Gd@R;6(n~%b+}*NfVF^!N}6rt&)3_dmJqdVat)Ogi-;~-oC;f^7wM+T5UBwSn}d(6&sC&-z`H49y%8{RpWEQ$}Da3B}O_2ZZbcx zWUU%;1Ip)%jC6jhqEsNcwZkIg2b)-nw-#0W;kq{j(;s@CYE+BvC3@Z#_-gdz+el}g zYV7|axrfog>r~v(T8~0ocf#L*ud{J~ufaNMiR*q)v>^C>VeyyIs`4Ne!^3|?UWv3( zeS1UyXy55yI;a>PFy(6Z=EmgAwzCCf?T5_gB|uJmr~V@$pMYXb1?^z=Lzw;z5ssYN z1KnP~xcb!zGO4n^Gcw&lSE;r2-RBYluRWjWY*1P4Zodj}X?L~S*}4WApo4PBx-Q|s zH`ja4pwFm09VL$48#otsUq28o8kI*omTf&p&dqyjxFM*_HZU4kYLjv!2+jXsenZXC z=BkOFtjsUG;~khY2%{TBgvWH&&w?Tv?zW?5%u5z7i@#;;iwx;8}#C=(2Cgzmlz)^;)I` zpY4>crxMb6MDPgB@%K}&L{?Xo!^^2$nRH&R(2;jrC`sjFC?V*p6R`clcH{(N>ZZBw z#IPSx*^GUJgHnb&;1*o8H4)WgpgyCiL>dqfypvm>NN6xs>02rkL#$eOjknw$-vUc7(EtWN~4gZKI=vB3#AM1Pjtmi%Q=P}0?$Rr2JoI$SoODgTl zP{>G;kI08~W2iA{*aVzXNjeF+%XDXZ`8z*~RHq_H_ozkX#&7ZNV+35W&&E{A@1DN9 z*PP}JZ#_XO%I_B#lCXB)QYe^OiFIGfQadz13v##wOK*eBDfs3pG*F*iZAo)#p#(0% zX!u?+Ki4$LCpIAh)D0(yTRS~`B%Ry%Xo~a~1Syz8jK?|o$5r21b-A0fx9AJ5Kd-%@ zA@1naqazj%&EO7RHWYaI{q$Mp(8on_Xfk|qVZA*e)bFB@_Wt)AURs8BF9x&8rWeN) zragR5${5_{R0*Eir0@jm`D~ZyvCus*&Mg!%J8OvrH3MKD>9kmkdnS%04Hy@`i1iLo zEb=NsHK;=9k!L;GcJ|%u(xxGYp*)%|qzGS!n$9?^>kHGn6b|MfZ$ojKa3S&$0ypjb~ zWu&So4M`Mg_XoYd05FtATd#{k-xBoVDii(5EJcqPxBKMJ&PjZ>9HY*&4JCv`gll}= zod)N6m&I2TZl(?88k4k}QAgc>2D%V4bk(LzHeEIELG0KrACg2 z?(xM>cUq$~%fcA+2_^i9;(}(Ld+-y0KV(i_MHFFBeY9!$DTOjZ&oVb(E~2ZrN?w7Y zE97xr${&#>CDoi8BmZl_9p%6&zF~qHzf>nJNy#OkvHmH{eAuT5f=Wxhr5DUfwVw=< z`-^3ENHS@8!B6`~g@xzawPen)Zwh@X?B;|nGA@LIKF{hrD6fmp&aJSrRWh+1{3cyd zJQ=0-v<6h=y`9&?>Qd2e*8Ti#hBFuai8L4aHGr0VLK_RQx_e4)5*k@WZd{m7Bci>S zPMO?O9Q{T31){l#0g{CA;&T2i5iwp&%NJ4hLI^vtyzxet0o77A-5r7aVRZ731I3^e zegJoHr87BR_%#rUyv!LWxWu*SaT=5ob}D)LB%#yp9;mX))W0FQA;8R_6q1jjf0te1 z<7NCvNp-oMeBb)Sv02JXPpm#y0rEd@RH@>3ShJaGW8csjka^q(^SI|EBed+HQ+@5eb`br1%rt~kkms>RHK={-4ko(ci(MYPX~sYg2emhbrA`D zqbfz^ON|*$`QzOdi>%avCpSf&FFy>pS9dB_yoiv(j@^a>{un1J$?lZd$$D8@sYfsp z@ipL3P|b5S3BIL*tWP76J!#f>mg+AeKa05X7io7`BtO)hHXI1mx1ry|#bhB0YUZVm z9D;2t6f6@5o#X}t4Bz3IMC_<>z{GMv$Af_V3Du)~en>V% zKTW}+n$5JM;@*&nZLeiI=WC$;rGPehbSZh5!Thd%Wk+TRC={?R$8Cr1uuS@{n!3=e zFWr$_zD?LphV}IE{i}TqYmdu{?G_ijR|q|tr`N#e-|KKdchQENc!qXoai{^kZ*iO= zc-v9`1p)VEN#y!d$7a8q{(pND{1=j5WwpvR>E=#;q>+YvbPZesCQ+mvTDyDQ#5pv( zlQXJ4Z;M)D0`+1tw7%fK*L_`#E&8bk_tY{AC0rPddEQP!#ji}2(u zR2=b>u#hHS3(+v$=L5?X6j0JVqBcSyy7EK~rjcAtM_wlmQbS1#%d}6eJj4VsJwBM0 zoJ=3)4lU068z9BDU`c)n>@=;7Sa^pN#EQo96+)`0X3FxE!Lx{lsnQH4bP2@QS&~Cz ztve8kzxY1ii@fkv@{@EF@Hcc=u{&TM@WoqnrsB9Z6RTRDR+ia|xRUD>eknwK_A`Ef?#mCi!xNZzEL zng2+>`gHz;08Ux;q)1L~TGs}-W6p!AS*j8BBl)*E@*|eLPA0i&>*dmA6p*L=a1T2a zStdp0v>9#8{YSP=$mgpW6<&yv^P82HPd+XxGu6+nnNY6ta8~vog74^Mwj*svP%i&A z$S7+E-UUMjzgS{L0l!#+fQ5j9zjyau3H*5vNR)d}sA%W}^in(z@fo@aDRLIY6<*(H!g^4AZe@$dxBB?KmDN&j#$JR{qtX(R;=;r`!n@WD&dP( zT*>8Udv5fCVW%HoCiX}zUTFuB?V~$zMW=NBLCFu_sUFMm(l5I#F7p}?`l`Q9n{J5L zGr$#{By$h|ANO3a>@4}>Qd|StkE41py?aOarp#hwY~C4hsJ-pVb!lVn$+3Y- zT_4QD)0Y+IC;!?&I1z#Bq_n^@L$X;)sP`-(xMH}31G|M>)*yB51@5d80@}1d$1$rl zm3(!1Mjl)5*W&TD`oTjD1i>?<OqJ|`(m}!WKo;}^jSB%M6Pq%sUhgg0Jn7_| z8L5h^87Ucacts7$ z84ruRF<*igPczKpuM-@ye4zxbf{^)DB)cY_odXNJ&6zNov&F2+3YiA0Y{DXLc++UHAC$n+P&1 z7N$^@sYVS{!yk>C$jB2DhOwP)2v)1NNJYj4c<~&@OO5#tAJ8YdJ?tg!dF*R5A6FPD zP`|PLpj1^(ov*a420wxzUqWDqK(Kw1|HM4ZMa1Sw@9UoAjeEvyWqhm2>J5eKnGq42 zIMOIGp%MJr6zHU&QU!iijjs}I_Fru`CiD+J4rX{ScVXrjfBO`V0_&)Jlp|JpCX!}5 zSHVYBeo=zqI}yS`k(rRk#6xX^n$NWEM$vwf)fHJ|vMHLm^`j}q22sN?5omCQEfjd; zMIXRwGs^L{tC=C?y)Guv`+RAHXF1?RdcI-b?t&~>q*Ucw27+IY`)pQP-_IM%m<4_p zNzp63HI+YBCRI8Wz(*~+CZ8i>qn?&MD<7Y|VEbw?zdO*MzrzJAGSEX8XqRj<3Vg1A7>tjmw!F_iuO#SI2p0ulbx zBRMo7hHbj+w-oZSUUYb0+^?Whq}c9DWE*?LDA^4+-k8A*I=;Y!RitLECFLEs5(~(< zNNgXIbnj4h>%4GDaY`2$sT(3Nyar$d2CfhTKC@^Amy$KQ;eioS-k~c=!J=VMuSvyG zS&W~!qHJ!sX#C>Nx^87`K$SYbrHd#bv*|M7={nXeumG95tska(h%DaA)l~>t(&^!SpB(y*J zp;=mT;+)F>beULklzU0-}2i8X=(< z{!xp%4MZfTkN*l1Ca&JuB?g1PF6y696T?*%cJ8Nr;aSvV@1OXOet#Q`=$O2CL8rqF zl%=1h&@yo4Q&J=QRf8uPw0}a|BgxCmY9TowZ@c9|b+C5x%6tnxHB^}RKOYxsvow;Q zYL+_pqnxWaEw{-UY^?Xdf&T{*NaL)WL@-U-l3WCtlyj)_(jTH`O+Y>5`So}j%malo`3pjJ>DBqO zrk|gQpn8Vh{81#MdM2a3&n9^G%ZX(4Bn?xp{drK zjxysngrixte#*%<^x2mG5cikDQ0pW_DDt%_E|$SFX)zrB`yU5JuZUSVJ%<9teTGjD zqEZBin`-$i3v#8yWfjcH@>?{wK~`{wvGi|}g);r-AB|L3dhtc(MvSVO|7?-0F%xF5}(XCyBC2 z#nQ!$`xceNXH`oSox6*l$DbT1Ww$l%O2D4eP;BsSrmu7B(yL{jP3FEp_!g8sHmzezjS)dH z_gOu8@KBkfYnk~{kb1qNbzJ3D$hxmq1?JAA%(h5WNcc80p$Jl&aoNH(u<*IUwt4sd zv|4u#Na_3A*pDCM6D^6#;T{${HE5ZgIAIV~#~zV4_TRR_I=MuN>SPf18PlciV?)!t ztF(1lW+Pue;okMvIo?rE3jaMQ7G|ayq$E09WhwC0DrEa1>Nn>U zzad>YD9BIOIj6NoF+6X1tW>}+;!VP4`cWq#`q_ZZZEMuopkU-0msvjXw^9ekO58K!(oV-pD*^U_q|!sRCz;!D--K4lWzrABpNeM|*YT&0 z(xjuQRQsp<7SiNeZ%pVOyJXd;xX!zrn>F0$K^Z}To{;|R*=h~m@*tftKEM&qI>!uq zJg?S6ERHT~g_1el)gL6~M1CUEGsy<$H|23a;o>EXrS9{}yu(8p4*QdD$kz|UfvFEE zh2F!3K5&YpCZ4QLNpwI-QM18si`S9COa2^`|Lt*LNK)CD?zoMdf;#TTR!9OYTuQuy zoUECg>7&e8W<&?I*KLU?0ayMuG4#?H*zu8)b|C5oxOP?RFiKL|0{b=}!empHQft)m zG4IhOM3+>jZ;PHhOgGia7Pfef^5Kica~d+}mhY9Hu+ZMmzg3k$_`dMl{xT=349c1L16>=P3&ahs-h&WXW=F77XpRYhd#8NdCfgQZ^zI}1iW)%2eX z^s;weYnK()H;o975UxdJeffu@G154!utnCCHd2E)9z5r)VmT1+eK}3sOnh}r}%*D@6*US1G`-ar0HYPGAeNR7DKdxhvmtD&YZn_6- zO8r#rag{_qnP&vptEVD_np*0}Q@bf9^4795*7%UVPNhj%x$c*^V%=cBMdL2XNIM7Q zqQW{~Qt#>EBRH1;FAxtC%T|W%(AMzAbjfh#s#NLRkocf#xYMWDq_*TCE1xt{!%WR| zVwz^@2qP02=?x=hjLz(6amz9py~myr=E)Dz$LiE@kWxpqo4B)tgGafFc3ki7Y<_wWQd}C>*%unR1wQeE`O*T8^G7Z zCuLN`B)>NEJ!4LiYHOpGD9mbJ?z`Q`7lfMPQq859wB$1F(-PAccOdm|hQ02uNRK=27+IN6U;dmf^Eqm9u7wncG4W$B8lRLt&53Xd=8^OdFrqDY zORh_*t#!w(YIB=+GQ4Cuty61Ll4%N+u{?8!aaC~dx167m`g#vQa>pb2wBBfHh(m@$ zY|+Y{vJP@@R!f4qcWWl_KFW;rFRPzWNz-4KbYTamr^HyDWM3urgxGJg?fj5q0+&#v zZBUfwn1*g54}1NkRJKqeN{Kwjd!+6B(h;cd)2jq*$3v@Rc32~`Ma&ouo3pOtTS;3h z!do_Mog)u7#$%6VR>^G3pbta&g)Nh|QG?cbiYdKW4zf72D>b0!-rGI2GsQOakdngO zP0g&wdWyD1D8iixA0RBu)rA=I^=*9C0a}fez^Zvlqzwi?D1sGa{*lZA){ZSIe6?tD z@ILW7Cf2Rr>nyq$tU;W}^W~QXp`hi@WfXqU8L#P4i>#-sZGbkBDkhpN-=eXnmNkwgw{8kRlYk>T#+Ur-@nTN5)?~SsG$;Q=; zGOvmXw^2U^Pw=ce!_&Th=qQG^L4}7MC<&4}zw{a!U6if9w8d&mZcckBE|r-=sx=ut zXvbQ`Wl^0iEZOp&G=NngF!6Ks{bW14uj>6pgY2A58{?kAZ?cXz)ttBvCyvP~wkI3D zcJD!51Dg%t?W5=5-$;UhgocEIz6S*f{(&U$N2npeKbds1eI!Ld|B!)^2cM9RTO0bd zCI%6&7L&9^F!4LSw|5^+hP>N9`fA1$<%C1ZB4!#rL0_z++twKn_6PAp6BB=L+#jG{ zE%aN(UJfc5ksVTq4oa?v2mP0$^dqx&_A9}0)!2`9?d{;61@-|SLYLCM(d)<)zBcAX z=a^U{K{>AFyIBKpJ0Sh{*8ue9?))VtPA?}5LqCjCkT#}4>0^T+xb&fK_x>Z#KibG6~S803h9y0{Yubth%7Q}of?vJ#+isASMvBJx zG+(UW{ZG@aB^Zy@2tGzc;81<=RWMB%lf?r6NQSdTf*s%%Le zxP4J1P&&>bY?d^6sU5kDvQ74ohGiOyLUc5R7HRyW98Rtbw^ofDsbU%}{Rm}o-`q5g zNP=SC@eEIKL4^&iXLZ8SLO0t%z2IGoD@9x{5m!U32XZys2j4y|8KOOqPgT>@i-;4d zG%k6Z_ccZpLpEX!RZye8$+1v)%9Q!rE}@jH8lwqEvN*|5CrAQQpV}gmuZ2gB89%c+ zSS}@D^oR_>jVXrH-fFg~AgV3fAPC+M19Q-&%l zlzPSubUBVils``U_2nyZrs+2@M402=ACBI;$@pw}IXlU#`VAtou$qIn*)Jsq_R>

Gt1V1wA^55c7My7 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

q zsplr3$fTka+3t1BIaBSz{U9}6g{?JRXT6THghoWz{4an5MqiGmZ5I){qsbJ#s7R+% zot?*pF~+=I9}-C;KT!l6-|bOW60YXf+KZl8gm;djoaX8_Quc_5aOeWx1EL4q)>-1b zV9A-vyVY=J;_`zAm=zuNCpm$LdZMN`F9Tt9E%RLYA9eHJ+hu~ehcDOP25@KoHz#Bpj_(U} zXG~BuU&H+Z@NjSGzuZ!kY|;k3zv5-G%oz#K|87(Dk25a_RJKYt#3;}7a8Mj*U+{4w0)^V9RQ=j*F2UaB5W z?B&h=?7(1k&fS+jD4AffAtyL#F=n2+#Qew3gy#8S9FiZf*YQI<;9HM z2U^kb9!M+%oHAex`a0cQH`^+@GNF_NBPJGDF%#lmCJd;jA-TcdKL=F7`Qh>zY&ykY zVeMMJf`8TZe7V}>BrYG3PUx2LN*kbIYeh8%uo%d-E6e+WV8njaixG2AQ#_T1W_-D` z4h=wsn(E(3T6qQx$2*^C$LlMVmmqNvGiZuA0i~5uMgsanp3- zC}Nnuy1|L8-JqXLAQP^ z>a0i4W(gw2z|^ypo$Z$M1hYV?8vxcmbCZR_BtW67U7e18f8i@I9d5Z*1bU}fe(v#< ziw&Ri8}NW?kkT$9T}9KX!jCHey!y7@qEfz~ADi5$<|A;}ob?>zJz9^zz6cYv*NQEM43&Xtw@FfYAzHGI3=a|BfbfqZb$hSp#nq7DP zady(>#DO&L=_t8VTr|NcwluLNzY|244x(%+_@Hk-7Y$(ZNIEPpX&;TFMrZ!|p+X@a zh4URUDhLMu(qwI&;0sVm`Mok+nDr21QaNhOoCjuo`a(}bSaEU-A|s>=CDSOw9vs=+ z9?SxxF7iFF6UzzyqQ>PwjBJ;npMIHtyqK+Wp%|Zc%#2I(v{7>ZZqK`}QAu7pWA@*1NjT;RF@hu6w!mmDEl` zun_c3klXokR|PM+sdEzkY4%~(ppgCs0QQi2ycnALAoEgCoFbTVV=ucO>-3){|064t zfq##BATMpv8{5}+&oIM>F{$po20{=2ubv~lvs&c(?Bn+TkB`V=erCSMx=0#Qg!?}` zYC&HUHbXsB=cL~MizDOX-G_c*1@6U#PfC2j`7-I{C8kgerm^SX@#4veByNyn?B?hN z-;Y&Bvr)F4C8jWrHdMThsim{QxX7&A@7r_0mXKA5-t{-cpD3 z7~4p=gEfo~Rvkr)$W50r%B>|K85?%iU3rTzP^h|#RQ-y2=u80XL7?$ICrbpakY9h* zDsjc-*vZ$8LCu0I$1-MWz zJ%03LOdD_3QBvD5=m-)WC@O}>z;BUGMr=l_ZBVXpsh?2*SjAUhRr}_JHYZdgSE4kW zQb1XyAlRP7-px;pcYIKjI&e(04eKOMxh>VsRJ2887YEAig0BbZ1JVf7$l*m-LmMwE|~12uuh=@rrt2WT_c_SB;uMIvaiKKFYF zT+2D0XYV5DsBo>ot%R+UF)kGJ-u-iEN(x~$J8uYM2sqX{Q?aYHM=W&yh1^;T=@%ec zI2Z`VDr_k0OrHIW&aGtB_&4`Br>gp`hXg7DaRIS#<=!%rg-KI^mPiJ9|9e>M<_=_-snaTO4WN0 z?Sp85@?i?>m!v>u@&d$#A+IOgopz#puk2;=;q-2BHY_l%*URH<;#H>j=BBF5Mlm)m z5+XkSIO%M$K7#70HH%}nUWM0`u;fG|>b|5y3qHY0@v)_Y6SOg`fC3SgZXI1lQh_zZw<4Ldd@IoS>j)dOZxFBC?Yf2&`kr@vDXHm_=bv_miZ zsgqXBA!PR~DsT+L3?;Y!7_>~DPk4oucRf()PSTjtnlSRcAGib(WVN8f#n-ahtXZ3A z{eisK_jrjeih+-1oo}np83;p!K#M+(0nRkH_U>bvsiQ+ zk!Qem1f{rwprgp2 UZP;B;_`Mu*zYP0sH-0Vu4|m0CZ~y=R literal 62657 zcmeFZb$k@d(=WVnCm!O&-4#OIh`YPHySoc zpL^dw?&n!LJyqYTuBz^y?dsW?;bHz^4S*scDlQ5D0|NlSK>q*_O8_AN1UUHD0{TFL z3KT396eJ`RJPZspEFwH2A_6=D0@5Qi6r@L}j}Q=0uuxFZF)%SP5s@EbV_{&UVPIl> zH39|!Y6A%c2L%O(frNmB@xT0e=mwy|Lf$|GLx7P0z)`^RB1fl<4dD&y`jvT|*OXor^qWqyc%XfSL5U{0WzlRIohRN>BA@n8mbHDWf z`*i$`W$X|lBB0e01|qwyQnrTXkP?`@9wrGhu7-K#t*lEoG^j8e`G4?9pQU3^yw> zZZETib{6;SYF=b7`4qSkwSwSO<;=e$E7P-am!T?|HJG=**x|qz?80zP(0ulB>=f|< zz&9fHB+4rWiQ+@%-v00fCCzzOC!6V5tLVt&RjbM7Td6Zo^{X*i_fWTM#^PyA)oE|% zQe~_{C5Y7d`{kSpkEZcZT;M7;)&s|~} zk)=Dk@@c+$A`}=n-(Z?O5j9{D9>ucbE8&0RqtPOzGFw=}`Jsv26`kWM|qm8M+T z4Q-5;LS3^;+uicTMcoj8>46g=L5b&##?)K_-#F_7046!^eu9gP@Z%Os7`m&?_x6XfEKgxyM^UEXF;M99ytrlwM1M!a5ew|5j~Bu(tAQnat$ zr}(xINZdNgDj9Uv#yMzvoLQd{fG3{IU3b?1xf|Gr?~*;*s>LtH4frX0z`g!Z+0XYt z`8)tp9rpBMN4ai+=6713fV$649@Vpl_%5{D>}R;O6QLirtv#jdkI0+dl*pTo_U^C?X>&nZ~(&7wz#Cv zoy?;4|9j=|{Rj;;(O-=JU@^sZun*v&qkr%}j^d(Itgo9&=3d?S{eb>*R387FS5z5T*@Sw1N5IQ12I$}YYPvLtjgtRW;keMcpQ?>!iTfNY~+; zz8^Q&H?Jx`Sz~*3%4iJn?Js_B@zub$#?{Z^Dz_BI7>gD<)-*q-O#blq*CPbxVhGLG z5}MWKd`Hz2p0$<9O0>)RY1}7xzuU`JZ!&jD- zY508ypj2j*8@`cH919~a82_O5H?s6eH%@p+5a&GO zcKO`A*6(j^e*=(1z=#Von`)r;nl^kPYz_WH0q?RO3R&%e29*ytugtE}!8AMh93vHl z4Uo9++QX1Q1&{vl>!JOV_~4SQ0fOr9_HN$V`shk8yYuh^E^kgA0Qn);J%sNGznlA3 z6Ybf)KpX+>vwr_DtEZZ0mt0lv4U0}r^YUekAqy`N?k#3WLSBCIz6!MEeaTifhRhf_ z<5_*RtJ=iV%|{klZXJJeTA6QUs9ip%Y0zH3T-m}~V*z8sFR)<$Y3S}r*$aj;sC)mp zM6>IdS0Gof)huUm)fnXfU~rt##yhs0@1J>a;;8|!d&i>8F3Cf-n7-*uHx#*ok9Vl< zG}`?h9{^xb$27$!f1*E^5fg`qp5W@<{=ivm4vyqsymc3j-=&*CgvOuR{iJ z?sdLq61xF{a1Z^J_B8Zl3sW6|Nk5|xoA)s@q$$ea4*>qXK~~hIl1_op`Hh?Hlcwcp z$K5IJsK|-|v#X+{_tC!Pi))qT@mLzslwV|Ewwh{uM9%2(Su&r|jGW*u*uUoUlzAKD z!TDGtVZcEOT}~rf30S&*Uk@sX00<+_fW|ey&9TmUVH=S<54X?Q;rCt_j_9Z(mPYt^FPZc&X%P+#4d-s*Zl>e<}=Z^cD&hQy>+WXzy60e<*QU zml`wtoqZ=&UUlo&?u(MBc#gjnTNsbu~3h5HuqAM+uAD-+1;e4xFnJiB%w_f34a zb6$!wH)p)vRB=;F;HD?7z;!x?i)+Yj1}Z#m@|{j(b)74SrqMqOtP)4X%`U}#UJ;`N zRXbIlE%A7-+c@v=acnH_~IQ z8RrEjGUvn^O!+W0`$VHDPWp2;Z$bF!dZ{TcRxUatRb0xQaa?0-(t-5j1R$1ak;`VL z@R%7sP^p!`&E3VwTzgRCYASTksbD|Eo!6;S{nHrUHJ!yL!0UP1)5Z;6)g8azq6Zz| z3!I`{f+0jB+H=ug)f-cUKGV}^`79}`u;5)boEnhH#ShQ35tHfLT1m5K=UGy|i27_u zRKd(K8LB+%wEm1fWwBx)jC!g7>`JV4lJ&JmyS%OX1$g+xq;~Vx2)V`92$_kuv^IM3 z3#+hKip8x_O))A`Cc5bk>ZYqXELp3`A;tp*g=x-bN~|)CCCQq{Wx1)xyW%E1-s*@8 z1x2hT@vJskWn%7s+(P_muL4DQ{9?3oWj4vVnP>W2xA>?Zdx$!JRxP97`{i~Aq=~_dy*u3-dL(%h>sXxhA_zG>5g{-8$EDY_vos*>TB+p1lfW_D5V^&APNU?N;y$YV#x;bw1JWB4ibuR=IWk zmgkFx+_oNvsy2XSdOtF#G#_izRrs;F?{zfY)d+*U^ppKwrRn{nm+xTN3pER!!3z{^ zWNQxz#&ew87f0}wymB4Dox)t!V(74{me7!g*TP$nL`L&(jBw*=Scq;Pc ze&1Mdlx>Y#q~A7KziJ8*QFBAET@k>u6AtMX`yw2ky+Bm$Lr(y=80ptaeFsg){Ns9= z^hG$N$G|s@M=|~Hno^I6V$fhg6_&XbXsth*rzZN92hc$L+}Lm2Jp-wc7?)hlrBu&a ztuBU&0qpUsc=rbgX+x8RojZ0550kxY<+IKP*rpb+A)n`^sg|uSf$@RhO->xsnO)3`J8wfnBEtl^k zM{Ig98UiLJ{cD{DZ|grPGdDeYvJi0}3CB5`Yb+5alz8sOAD#f2{}}%~Ac?*ALgoG1dQU>VFL-U^wJMFYf;+H(Rye zpJVy_y&~r)wow`{q>-Z_&Q zKG426L5{9$dH_UlNP2i!M#5fKUg${CV3AHfNtGqfcQkJB7(E?0i{uyf`wx*tCZf(~ zT~nbaGpl@P43k;*M`dmdu#|t?LriL2szgcFUt!!jNDsE0Af=$HuNBfkVwu?zH2Zp@ z6>jApK(#K5w|_;$0Nw>mW>@ zTqxZV0oDJ=peZWm-M|p!rjxDuZS1K|qokY5PBYC=e15~AjF>3McF&Yj=H=S*0Elt( zRgE;i*6e-e5(80jGv5Vyf-B)D;Z!V>zQL+3XM^^E9rFyH_Ijr($^0^2r*u0-r}AB` z_y8STozSZH4*d(+ehXS#shB4!k^-^$DS`A0XlY@~g0Y`w02RfrWkNwNL_UfIsn-%| z$Ei2S?0WG~iq0N5PtG7^5T23mrS$;?Y2nE(r*@J>=^&@KCuz3Z_{wv48w%6m#AjE` z0}gBHGDgL7H_*lG9eYP;#Cvrds7*A``ACuTrcRq8U38qcPplQ@0&j9SF*0nL9dU>s zA49e)bQ)7H5)uxFnpHyCt%U@L% zJ50hvMUlW2>#W%{T$DZPHR-r+k!vrPk|{nE$r&0Tn?u-om0XQQ?Q@q`VHfh)PzNwH z06pa)hqn~CQspn#k;vjKhuIPWBSR>0KgOdR3!wyx8+_jO%s$m3E{LalO~m*FO9FGy z&76NFwkgZGi$YL>mvZ)41y9hy(8q9GxpcKb= zjq*E}RAGZrC#x^cNsqOF%ftOvR`89~pbALhx3AQR(utKJw7#JUrj%+EK`0Aqc6|0(_LL%K}U-K-~CHM4%OKazME*}RGRVVqfk9OAd4a1nE57+8P@LSOO_b1X5 zu6ElWZHk&vU3rCUt9o;P<0Ntpdq33Gdq3^0&?Lw3$b+J{!d|S z2X$oSan{c;@~{pR>91y!lax5#S3#`y{!>u@9O@60{GEl9b^#A&lY!_Q;^14B>fs7j z+OI@v0>M(PBUv~39xrY;+uX%N^Mi1N zFfz`~8m1~D-0?uqxvTLi9wOI1ijHirO%2YHbJ(GHESWdBs3$1l9M8R&AbJ4cJvP0| zF6W)vz4QvMF^1_{dVdPZNqVBR(OS>32Lo)MWcI$g}=r>8Hs*5#=6=b$(v3pcL&U-yD%YE`v}J zM=5M)Gd9UX-rq{9E_{|G+=86kw55Wn#Z}GI1xfbc3U}0uRW7KO@_<#kZ3gdemj2QF z*X-i1Tsg*zj^Agqdqi;Gj`slI$trW6@IAhv_q8dUsj8oCD85yxa`&R@nsl3IQ@r9)kjR8uD>#ksZyPFeg7w}GML_|G*eqt z`A6k{0I!2Zt5G72|k>T?=NxBOJ+# z=0%xz;lhrsKFmF2O1DiQa7keKy7z6cKl=1|jH&ybbP-FvPyUoE#_`~zoC@j(fLyLW zKCdyPo)><}HGH%m&Q7SBN!aiWUnTLS!+VjpM_p}>bW-t5mtP>n?v>_83SD-z zfvPr1ON9dUr7>r;O%ex_txi=d|BBdsYIRlt>!~mF(}?+NT~}?7l&|)FVp<$Wd+C_L zwo-MEeq#Sb9C`l2uk1Q8v_G}=x}+_Uwf>W-7$LXLuH0m_6wc3aUl)3OJ?1vl}z`U*hqKa0O8q)-HdJh=9+p0 zSjI~qWZ|pI=pZg@vK&|Ila@u#yef(wsmY6&xhWT4KDaF3bui$fKQzJea0|hD=i9Dk zcjya!*k%oe2f&($udH1{1Jm=JHVfP)3Fr{>S_jJP7N3tdVs}zWJWgn&6t&}$(^zY| z@`RJ@kGw^rl~LA5oA#g5#bYtqk@SZ0@T%`|g*$?2eC);q=Wck%l;|yxhQJXKrrp~! zm<7Le?R%%afd8T8YalUlZt`+EpjGmg8!s}JmNCr-QuXu#KmUap4wrJsG5Cq?Gjt1| zDIh`o!KR&N#hl2z2PW}h+~;bw-45K!Im|CBwi<^>6-ntd$8RI?ahJWnon_00 zcco29l03$2!VnA}iM~-^2}Ag21Q-G8SP6DFVfXMkn?w- zB{QVPv<;V@2y2YRrJ-$ngdf_{^~b#9y?^oaP&<1}cX6fBFq(i2FMhEvo|4LKvimFy zB`nk|$N;~ceo{Uy!Au^F#c=vD)tn@B6b(MT69NlUUqDH{g8R?!7>0Li&%5VqLKb{_*OLdb=RuW4F zDobdM^GS_8m5t=qHMG{@n!f~j1&4I=^?Lx$LK! zzC-NLjl<|3qdDxcYB+mB*pzyvyvq@x-YG2p2dJ&Da*M~7dGCi_Q_ca~7doG%@Hx{IJaYTKMRvhs!Piut4o z*)y2bXZdpw7o78P(#JH-qKxp+yS$%VYCU_U1`FTIr#*5yR@XoQ5=0y=6{W0@y9{Qb z&7Q6kkHhJjLMytiKo;QeIZtvAd8 z59yhw>3GwGOmuN!TG$#&m_U?V&u3*`IeMyGxqQb4#VAl>fB2%XnRk5>!{qa$O1755 z7Lg_f;Wh1{uYtF}jS-KP)Oga!Hv7qgX7qGI z6cRgzOkYdSSojldG9J~HW*ZoR#Fkf3&pMvvG|iCV(wwNu9mLVE(9eZSn|x?Fcpien zKi#B5a_tc|{dj%1RXrFZan~m9R2{3Sm^?B<=l+RRB-UBsSf!#pfLSz2yup$gGkz+p z#!lO%B`o>GeYH;UD`WkPpt@$$j4H-u{UhQ1{%Mn4e<&sJtd6cKMzd~!6l+s)8I~ni zE)S56WNG3(KceA4W5XY<8~X_Tt;hxaA;0P11!9)dR*GMx>es}=XEc$eE!5tkk1CW%U*vh`%SO_K7G@N!`! zua>rvhGOBi#0WW+Svjr~l`);dR1~xMXr$z<*8t|66`K7sD^!n&1L{$hkqSX}+=UZ% zj|{6!jDvVxN1G(2C9;y48$qM8#s%Kd4xC@MCii)JE+;9^(o9MQ8qCH%LsR! zEZx))+n4`9AilE~MvJ;_egL=yxFwdHnPjOKC!24kyMHG>qMZ!a{9t}ZaUwRtFt<8T z85HFV4%fE}`_N=sl2we8(u_^i)qhRMzt2+yzx~3#)!J7Qzny_k4G`f*)v;WE3_6>W zhV|?BMrwq^j}266tAW>A4k2cTiyJr&i+YCgD+-}a3~jWu@Ppm8)fE-riJ&l((n}Q; z%%osreRZ|p2}KCam!G;p7qw2n8~X_}51CjG&k#Qe`~ze9P&Fr{GCtK@@#f+wR`p=Y zR+5cusaF3lP+%j?;VsN5yn8$x6RH_a#Awb}ys$Qg$Fy~mD`YR2ROmb%FidD0bDl{J z8DO-?t=IC>!6HV5ZD#ST7%v`Ch&zlCZq?C)O=+iD@@J4rvlYu^c*Y`}tbVmYYFAG7 zIEbTA$$;cTMAg%>4-u7Oi4#Wf75>WHx5wl2bP|%dW3+kwiRx&PB=SZQ^spE+_U}~j zl@TJ9Py=o+0`O9AfC?nueRE0j9hBc5=~tWI!y_;>tgv#9epyp9%fZ!< z!>nEN8{OOYM{!2bkn$yQOH3a3pI3JK1+N zQEeg?XC>hN+~Q4D82@_A>rPt-1lJVT@W3dQc6#}1fyL^lD%N3iQ|})D2E|vxUpl{j zKc}2A-v%~CHL|%hfl)~c8FlnPVE3a*q~hR;46(&FN%3+=l}CjQLdj;!ZFp2RJ>kYlu=57Hg+n8Gpj6kWYeV{=Fx)WfEmQaJ zWWR)O`k;cY=Zoxsltaz__9F)=R6!(juf>3i_GJ-gx$i1VFQ{B*Yy^wuirT3IrOCn{ z6(o3rk^rdlc|sa(z3(FfEau{GD;tO>7Z;Bc8Y>yNS2FXRtka!6G9)6Lr_nvSc+nD$ zdf#{;JKjZPZ=w(0l7JTE+Hdi;$J}7@4N@juihR$^pmFj1G3Wa8qxf43l9cqb5cKke*RT#gvK%Qb6lJ&8xH-Dx~Y%gH~ z{A{NBbElr?QBz!R2hNU`kUipFm&tnKMafeG^)mreBBjL7IV!jG;r4RG9<14V)>nQ)7I@;*!} zST{iiEzF*hDCR8)8BF(^P3#rT5=a_Ux*3p7KT8iAZ6b=DU9U5JoFXUIP`|xJh=(>2 z_$as0?bu;sX`Hy_(I6h&T;-(#1O_^Ok40q(W`w~Qez(tZAcf+&-2(2E3F0Ky5J6X~ z5u%Gq<39Wbe4~C?!UJF*9dxUT8x;V6{Q71Q9Mq3@b-=(;A<*O@(J_com<1G$h?rRU z<@62gp)g5U1pRBeCP;>UD$^BuQ8)D5e?43s-J<4~xz-%ih~H2OCSV<&xP5gDx{S zXm#+&TSIgBTsJQ^mkv8>lW zm(lPe>NHNAfH0$#?#rEJjC}c)puKSmc2rDoLtM>f5*7tv(~98rAx1~Qp2(;Q$;Gp#K|T_7hlm^gym~Aq@KbY z-gex!T9VKihRH}}mxSWyk&B5lqsiXsn}vu#lgJu!2rsc)qpC=TV?qn}7%i4jgkoA! zRnn;F&gvV5$9ZK(T6#A^COQJC3J+vwtSQE$gLX?YmC?)coRwtC!e`{U$|#iu5vLCg zl&$3AG~bN3AF|#;NJMTRq(M)?fyCuQHC|e+&cPf#9EDi9gdFA3~3?#~scu)+WKhx~@csbJM`)Q4I{9M?Q5@nqPRT43yyxDI3ZRjKXr++x?vpYMT zqXB`Qa9Uli5ifaj&ZC-7#=VyZJ>j&qd4j5{vM+JR^QBl>9v{;%ZYb&Ylyrv6`=aID zlZ)I+1kqbivcu(lwy(f>N6M0lmiHN=jb37rPt)@ytWRBimpn=F#y_=-B$;kXy@4lT zM5pSa$cIdiLbG5JFZoFtm-eFfF!i+=B5g1SA0sk`>3=czUw}O;(MymIkQRkw$0nQp zL89m-KDPaMrZGS<%v>lCO@nAIXDAVftAst7)0+g%R`PccT*fGFTYfNs`A^Ev^|88s z2T%-GzmSRmC8K_~Myrog=lnG5YFk{&3d3$dG~}`NRbd+<^gH zfN0*BS&KLD456ZqyQ|xDj0IIJzK&!HAzwB*tR1(lMPhEC#*$a0E}xNCf@fpbp66uB zOO~xAa-}jW*ZTms46Ae)_f=R4JaHVl)v!*SXJNy;VB&T zb~%hHWcooqP>PpVFC3~o+VI1!E7KUqW2Os{VV(&`WGAh+EB}T;dGo?yVpo4QlTT!6@#=7F8YRP zaK#52Ek9f%2;le5OoDnIQQr~DXqJJFhr(Y-F>(Fx=mZ!>;ds$lL^M#cCTX}houNrhJ9>J34}17%(wRL~2tUsEqUH0TZAuTRB*-id`k#gIosCx&EJI6@H+ z)VC)hVPXNLUxT;_C`?f9UAN0+6C(A?`IdUYzo*{Gr*y+^=1TOyTwBcN9x%qEI;T^4w%x4i`WL*HJ(zH_9&XP>%DqZA=php0d<8}a$V2CZ8=`t;~x zK!7y_?(^NC%VA&@&q=zMtI+#t^bb#Ac=Gvcn(^sd?qVZ85J7_;rznKmJV?`xcmQ-o zu20l57k1bku*1M^k#Vw_!Hr8g4C(vmX0$prtc+=R_6}pu)>}}RVCZTM)E?>vJOCE1 zeR*{ja7jgBd7Jk0td9LN`g{{EZDubJQC*#%d{}XjwL(Q7&jqWEQzr^!ZHJ}Dy%;eL zM}5!h6G;T|>}-U+SrjF+HMjEZGfDx=;?+EU#{mKnJ?t8vPIZhEMj> z(BjAbxF`W;;+vuusJL5fV$ewh@c~C3e#rs4#TsF27w_~F{n4%~q{1%3Y7?}06R6iy zGhRK{cDUz?MSZCj#@=_>O7%*g-M>){&-l-k(Gg?;-{d2kecT5Cm-j4(T`p?Sb=%H` zw4$!%HT0 z4qGwcREwVdw}e#o7n|Yn?Ho*5gkZ()b)}2r76t#equI_>XjvHjcDgNPS&!k1gS$)D?aJJ=Xnr!4wRq~Gu$LH=n|x@m zos&E(%2$ka_Gm8DRe^);v&y6IgH^oBO6;Iizko_vXDm&oX)scKlcTcZUo;PEqw#wa z`>uFeQSG2Df>z`*%Fvw!ZAzqP;DEWDN))?)g=B0VW1TWLzry{a3Q|~B!_P+Y=Z=s_ zUSUjinZZA;P0WX(XSZ~pTOVyZR@b+%2@~<5p%%{+edGuaAMyw}Mv4{(0}* zqc6I<+4jM(@6ro6l|pu7R}}Edgq+1}N|9!WfswG?@xhQif5Qtn<@K?}Uv{r;>V7GE z03@m2mUV6_KLBX(oLXtVQ?IX-eFyUoaMs}5QAmwiF_L@-%kP!Hs}leX)V%wQ4m=26 z*EUM`jURYx6Tjq!c6w>xn0xU!K>wMH4*<#DxorU&D5AEP8N}tX$Cj-3P zxzPuJV#-mJ&@&kGq7EuyO2Hi@XJF4olnl$6Z*V4>>Dg45AZwV{&Ne!6iY0N|-dsET z=LCCRMRY;Z8mTlX&Z|#nN9sY_+Qd~&iJhH!R;p--*B6ejJ%Ul{nPjE8zK-d_b8Sdz z;|6^|eC-moqp^Q&3isY`0iv=e&jf)>3|SS9Bts8soxGNro0*)Sz$Jq(iR>vOIi|Zz z5oHr3XEWqJtGG=0Houw`m-&@SBB04;sM}aE?E#KO_S9`?gAip@w(whcXnj;s?JK4NkE(FJNp>XKUSm-yuDu%?n`V!;%Fb6%mg z=U#FQEN;8mW%O|;p0{m4WNvSK>M7-6doF%58lI=Py?8_Oqy$w>XClZEG~+YkJ!74p z?}%eYw%#wAxd9xTeJy0Vs#+6-gMg&poq?9`!O&e~A-u&Wfh?qHyJcZ1%BT!wB-%>C zz>;uhd9>X;@)=>t;vUC4Bhnsl{$I%RB%{VIwJeNBOK?usFJ#80Q7QF>blf`+iwmyy zZb-L2Ylc0(zkU>*ZkG^N>*AN?aX+ZzZh$+K;XVH8&U?B6f9Wu!?b5riTQtP7#rDgi z*4vt*&-ZUTL3=-S6wt3O;1Ft%|A;Qk#4#Xx@dpsS5Ooy;53X=?VMhYF+J}=F6Lo z0zG#C~%J1T|)qbs79hoStjTr`yGMV_s zHq-}1W^3!4kXJOhA|7kjv~XKaB@v036`M;tSFN> z_m*0wD`HpG28m@cj}ZAmsY)}WQ~(?SIswaBM1}gX%5u<#kI4v$YCDKAz_5V~rKqyK za+9zqRtJxv5sA|M%nH;p)f>c7n9*Z(2oAIMc&$3k84l1E0yBqAU!cK;^Lwe=yRYpg zCkfaS$$WC4Qn@9nN~EI56&7>XOBES@y#$|-B&SyA&pZr8rH}ds#S!(y5F@o4KKWF> zHEkDZ0{wQ^0#;hC-K{BCGCwh)Nh6%?TdV`9sp8zQ(^kxQB+$yCvVJEVja3LXeVtWH3a!5;MrW{Xg+o_U5_7- zX@eV!EgQoMaK0efp=0NNgO*4!Q_4T8w7CHbgy=4_>!6-_3#BAjT8#~n(kkT0<_UH> zC_kxoYyFMqChpL)bhH2MR&ZE6KqO8|M!BOp3AwU&uF1~CP`VrC6w>mqN0Rws7i zo!c&bTSOk!;WB$LAy&!*<8z)w9C>V{Y;8UOOr5hibn8=G{IK3pFM}7gBwj+AsIffH zOt4s|%@w`$A%!;Dh+WPxR8clab`E_9xIYr;?+$Thf%=s44B*~lc&-v7F9gou2c@ph zRtVTBgR;q^0RQY${bK+1>aGZpstAxQ-dLfSs=s=H!nnDh)gC>T$$mFT5t$m?CL>}F zV~Upw>QWi&B92x6ndHV=`%3=R*y-4coCJ|x*;8WOtp|XekZ&tvH=go_%md&9M#!dC z$>)nHxKX?oFFmI?X`*ruof7WL$<+&r@IgD|$E_mpwyAME{YeUGaXtJrFa)!#!B6yf z7ytV;6gvS?exFGTkr4{Uq5fI4*Vd~5Oo2KurIJ)hCcaw%G^go<@-?QYZm2>j$k#f) zf_XERN^pZ{R^EH6>uQA$fX0gA%UhwbbIB@`P-~m@h^LU`2on`uz>!C!%8p21{2l;4 zZZeUW{P?kRa;gy>FR8Acf$m@{LWvN}^)lvR*XK_IwB!5*u`kMzpL_r$BxZ1s7(kaO z8Y5vyPJ-Fc`@M0GzRZ0^sU0lUlu;tjBknfT1>CL9Y0;BNFqdBQ9>sy ze+NdyMj@x<(I?_#1YR9AU$OM6Ep;Md+xJ-)FMc~ALhQn@epn*tE?9m?Gf)B#cmmco zZeo!$6XEPS$^A~;9)?V=gl?0At1!UNp3# zQa>d!@5v-Zc}ubwtmeh1_6futl9}ZJ5;FFillSHk0=^3uNGVmes~VL&>zyrcqZex} zTb550HY23)Ru7(x`WE@D`K@6;SF$k`o{A56N~9jkKNx*XNIhz&G^nx$1qbMUiBIk9 z;6>VlvZJyoCQ(}4i7iD)z(ORSsdTx{zLfQGlN(~_F-=(E^X?o(a1q?MLA{%V2S~#c z`!JLBV)p136v*PyzO|@gVEws!G*A6`nS;;-fWgGl%yR1i$QMkdSshre7vXXI2;-aR zl|cB^&q|yATrtYT@GSK5olgrCWU75(bvf4{&Lz%vHhZ%%TUD=TZ>g%m^t7S85*uG*W;W^( zy~WX$}mE`=O%gaQ@C|%A8(0@J#@Gl@JqHa;DB@yk8 znCz^FucBg?}S#7&t4EXalVmxYv z&SYOO@a+eXC1Grb$6oML$(I{CZ0P%Gdyr3-!_2YcHWpB&*K0Pw<{pNC<*RhqXo=D4 z#%tR+q*qgZv6#$eAhYR=*E&oa-T7iM1>)d+Dcf7Z4+C@)QhHx~3LgUOpGxLGL%0yg zSIIWuR4+*p+;RkZrPqHN3dhHa1?{x||EVz)EKmkYM8h$9n8f%kpgsf8i_%gn__RxA zF6?|zZ(2hRhx*+MY^QlqPZAC86L(7&u-`Lz=t_#PiEP;-ZCm8r$DKG?);!q|7GF%X zjT0~N{iI^JPj}A*?XmMhXoLJjc=lEg)gr)p61gnR_yJJe7ek}X$wjGBu&E*NOw`8E zns<=Z5zqWwJ|lHWwl-?HchNi1WQ${JKcADiG20lYCA$8GYGl~Dc;Wv|WyDpbmct;i zO7YQ3y;p4kMegY<{x`fKTW04F{WPX&mGe*PwnvVRj4mGlK}7y)#=bElACXvxXZMj8 zPHj14Yf-Xaum1}0tJsu}H%F1-Es?J~I~;5lCM>)qdMdu#j;LxY3QtAX06$0x2rIZ6!&xW_TDLItlHl_P^N1oh(I zutK~d_tBY`ucZqXPS9C%Op*g)cv)mIX2H`PE-0)7S4+#uzI>fUSf-(DDm5+$jLwod z6oMgp!ziMP1cu5*sg4_&B=JV>Rft+X;^0OMD_a&I0v?B--Eyjv4&SF=sxm#Gj8>}$ zNC%gwNHtjBm*|$8wUIx%`v|-()V1|i%QEC(O3!)4;SsgHIo%wUPU3+hFck{{KpmK>UR{{d`QIZTF zg;~9M$smK5n8FFbMwCmuXBrUANR?VZ8gq1u9uLrF8W5rriXc})k|!d9eI@A!0a>`F z^j}T$e|;kSK?8mynchi-sK{Yd5v+|l`11oGR#ykTrR20O`@Y@VZHVVNt}#+oY;M~4DLMP#Ck zl(u_+KhUfzjVetNy?Uz}MwFP==j{R{)Cvp%yc64i@Pb6Z!Q^6&Q_TNw9GE6!Iy7=! zo+4NC=~@v~WAr^&96a)t?|4%2=Q#d7M?oRAiEz==VN0ULWRz!4RqrQN5>+;O4=Yl% zXX>O(S_Z)?A%X@t_Qe+Bc>PEOpCofwCJQVXxmyQLQqh9T(wuziS$d}e~kh@kt#;RG(Dho^=(P}V(Y}MRymQWvDNj| zs9C{mIc&xOdpUqYR6Fim0KY*Ai~q8i0qjUZKV}flrhT-QlY#3@m>jT@ar%mmm@DXu z`3<$yE@j)jUWaZEF&jGFjNBW+lu@Rcbhj`HxNGCSwTyyXvMdw~(A{#KC*$6B_lZot zr#mqlw&zzIAKY)s&^>e{FL^?qxOTb93tF%$bY6-c-DmadO$$mWRTZD$UT%MpR@t6J zU=!gOpjIX+VnxT{$2W+HK+RfpLRskbbRQQo#X0ySfb`0=DHD>z{% zAI9Ld5crG5Qg!I$v^Mxt8gDrbkm1v%7K+L`IiRap3HJMjQAK%`7ciuc2L){40N~C? z%J`gZMMmwvg!YkRfP85?6IVOnKsQ?524_?-O7mHrdQ6@U0a{07r@B5&JnsLE-H zmQ@G`1|TM)!gM7wBOR}mbJt*jGNghMY6s>gpAvm+H}NvV(f$QRb4IDZeyp{mV+VoeWgBU>C5g6=~&)FQ`rB~BZkRiqCj^|J>HcR&z|r&?Co8m zbe#kO>NSi2rVr=c_DPA7Qjn@7d2iks>P_}7DWzg5WMplUcq^8O%?h4&WJ&5~2Ax$Y z?Kc4Jj#wkt96V^TCS=fAlbCIY&A=)4@i=a4hXZe%HK2PRNwymwkNoRqOm5@tuqSCN z$HDgvOmG$pGl)`Bi>XPC$MR7QBa6fnVm{S+n@^~|5J)%F{7MOFmayil>}sGYN=ogc zo#52uc@>JoR6StN$35I_#EIw6L?u=y3{eh+)Lc;)WQb}hy~kZc0gvhRgoC7nE8Zef z*vE|cIsY#;*FUyBbmBLQW-P3Z(pD+3Eb|7RS3qZsft284beZ1pfK@uBG~yZKY`0zc5aIe*}1j!d20Ym zruz8o)I(uVwbCzPKjuIA3)e{114SgLg*-b$L8ILexmQo6*=0xor_nH*o^0S=$m?|| zy8R#iZNi2vX*8>KknUBHx-}27>=|`R&I3Sb>%hAo0FvS61MGG^?por}x_s|et%kf2v5-;`HG$n4*AtfHfR=AL-6Ed`;lqt*|D%p)7 zK&L3LIR(eaPKuq^^>euywh9pJh%rY|B-xY0O>_)~ohVzR!E&PCB-L=IS1KwbBRTfV zJG)oV;|eDOacrmIQGvX(%n4n4Bz#m;9HWSI2)o0;9^i*zL{zm-AhiJBSQlddE)H}P zhmQHq=ELjopc&CI$Ifp+7LQ#5Qv6w$O#j28;ue{C3MV-oC?!v*MFtejn^@soK&(-e zD09!j1GUpngM8E^;${9R?xJe?A=Nt#+SFnJXd9~qVT6wINlY15u5U(hlALbEs?$7p zAo-gFiTyWCq6L=)q*0y(XDT9vglQ?dkG5(nwAVv0Zwx~s%F@g3?(IB#IE?}2j3o%RpF=xr{b z&CMsM$-``o)ny(Knp^_P7s1Pn$sg~?^>vD};1SWu#Je!ZiQn8|gv$TnWQO&nT5ANf zaqeWEDQuX3>Hjn2FBt#1FB3A2Q4}Ieq*%}o2|vl&qiq#M!6)OksZxf zz^hct|5@Q7OX3<1n&gEv#*tQthNeK$`aDfjylm1h$W#BFM?jV}0&a~-pt5EHz;xmQ zhtM$EI+bUDM>rBJ>*sa)-%bx4IHb@78D5oQiHekzw2xnm6WY$3knr{3H6~?l#ASfD;;&A-0Ihu?G|`WvNT? zsT9`t8&r=h2O$#)Q$Y|2$t^mF<1EJ{FabuEag*-leJg@ ziefn>576P!-OUexx!{L5f6?2IC1~72_^^N%nxFvRtqKxV(YKn?qVk8@WcAM?d?CsV zl|}V-iviXt8YF8SCfrgA6&*#nS^1{@^%2iBs%TTJQkh@z!(*TB#~w+dDTj!0wP-#` zbUYe;UZy*ZE%B(Ij88fJ$~3sXvO~KynOego(x8)8cNG|dh_#O^9p_uPu3M@KKs^or znDs^h1lI!rz538o=yt!R6po8q9JHdibA^4qP9aKonB@|j8q{W`It`JDGRGM{uq-@? z?5$WiHnr}RSO;qjHM$gzm*cT$w>?67+jm3r7>OmFlUS0iBC9kLlfttw?5RSwORZw4 zCCe5nMF+JNuVM06aFVncTSyLdbP6N%id$SSlw$O&5DhnMUY z>vo=?xbwcA!R_MCpod_SirP0^IOVpfL9xEk~rOj1rnQ)5*!+ zqzS3JmLMsLS!yev`ZVFreMqDievMW(Zb4E4O_Rrc3oW5PieJ5O2m+9M8|?`+`au_& zD+rO682M7&Dj&P~r0_|Td@)UthpU?oghFKZ$eN=FzIP$nkoT!i1`xFSNg-LB1}FHf zG;^0~Tb{{53lGpld<>0aYOlrW6*7~pn@w>o(36FnZz)=6J{iDVY~v92EJ!lN4f6A7 z;|__C;jQStzGpY)MfP}n(|CfQ5!M97(48n*%KY+05$!{{8TK8C^=^U^skYcfK2_z6 z3Vedi8E}aUNwSAIsEhM^^7Yo8mdG-BF3t|y%9ZBq8|7&NN0~z6D;-Q8DQzL;i&Wi+(?F->C73CG)h@g zVZI3w`Pt#Nx0isB`wt%z*V9jZL{*+N6X zf!-x#N=65<=I&)DGo334&|nfmX}{93IJ0Ht@iOh?Fly3nM3uFc?hM^bjSV6K&Ufh- zQ+e3jUkeLv@%cxR0^f!>baGtCcxnYO?0qhTf= z8P~3xP@mYq5&8b-IeTziB&V}J{)~*`)j~q7I{?OAUD%}1rw1=98GFjPU?N2>W-=K7leEJ&HKZk%SzA6ir6s={nk0MZU`g| z<1Dh+O>QWgRfYsb5>-o*?^y1e{d3_u5qpbFk;488aj19M%CAw3(Ij6z`Ng?k%BvH~ z_+$zR8^ThWLBt##L2s+d6=lO4_2N_54?qpY8yFs22?cGraZq|d`Kf*yiMIkC|Mko1 zF#dhmaVZLE2J7&*&F}~q5;7BLRe59YUCOT5r`nD1MteC&n;KnMvr@coD0Ec1sy2=U zmZZjDbwiO7E*hYsT}$tqzlkyifJaGYyXq6HZG(azPvugm0+$6y?%brqCi%_;%iXb8d+I%4ne|}W>wA*MY{Gp(Z1N7qnx`Pd+*S|$oG5(*Rs#X2V0ubxD zg938U8(z@5aVOm&|1}%3{9u2@HMIje8NNpFn)15F>*M*vg>D|d zPJ>vV`2ITnH}{_$cubqu`Y!u1^TXq&cv(w^BZWN1Y(j4G4%W7u5o1rPcBe?IQ*+wr z_!?fN%l^+$e~Hx)vO*CP#*KkdW*KOvL;s)C{O2X5k@)tr0&>3u=}rV5OTNQ)p1?R7pqJ3UCe{`>$mG6NmC|z8X&8 z6g_^fwOxWwAd+ec6s_%P`0>z&P85BR&S*70juQ=Y?QM0Ls^q zQbB=trm(K|vx7LS<8)Q0XTEKg!9Ke)6y)oi85?U?ZoiNazy9a&e-&Bw!!z2zVOqYC zZNS+r!hWIAA(=M%RrkM&@@Ia3A_c#6tH9O4eQ8DoJvsa(H9{=%Zg`Op;yvR(&J;NH zc1e6tN=zdEYj&nXYZ?Nu7v3zK1k9k;OeQMDA!rWN@7ch{3 z@IX?qFKW3?fKFKdAswH>ZGFV|?$=co;&s)PB=J>j_Wg7pLCgORNPyoC*kiNr?W~m* zidwFxb8LvGX)x9o$gW!pQ~9qY@hh{=In*Myg^Yijb)9AT8v=;54J@My0Gzr;_`Mb1 zUwe{g1)*|VKabpS`z2D*(AXE-Ub9OgjAmNz`=5eXYeE)f0!MAYCfh|1FL)VeKfQ!3nP6mdok|5?_NvOV<%6Sy~1#2o*rW125Y(}BESH(c@-u(b>KUri%D;nUUMM?ybCW#<#oKNRxV=$38*zC$3f zMJ_rZLM?j9OrYV1%5v+hA)*7D;0islrXU4~ zxZ76-4LtjFXe9PbsbZ%Ds4n#mZ>uK=*&;y)rp^M-h&jxFu46)VO5`i-MGvFoTy#{O zhMP2qC$Mrv^}18-Mq0miGVp&l5PfFCiEV_yn%J6yTWnJC0AUk73>XZy>!EQcC-vxN zO>s~*@P>wPn@(BP&j9gqK6>B2`20)!NH?B+=PWYM>)<{*TNya3XLWj*_8|B@wkhiu z+Z1!{mt*ENa4KJ!L1_6St$w?F8c%fOS!`5<(33peZe%`k7ng0{B>PqtNu7jeAzpPG zfd<9kQJpAzMmphAOo1Lu@PSrER=jXwqDjgO1$VVh-+7NM4xv+t5%8(Nixl0qTNb|;W+;#nw!no_8 z?)^84M5?5L6%i4SGUVo7@y4iI5RsfSlTW;%R89Svq`3P7mO8S_eTO?X4R<`CaZVW%`p%9Wu zV{Uso1GDMubW`>f4>mjR!_5Yw(#)z>RHTCo4x)Tb`9w;80GP4NAjJ$Fv0!^QKJF%( zq(gAwl-d{VyO&>nk>rcupE5GBj}JJ~S0;?nW(CvgWl_;(AwvgDovh5ML)@>KQzF-2 zI&Aye06~=3z3Tb=T5S)&A^C*e(&inQ3yQ{>9y{PeVdeeIo+)CwoN%ESCh{b+Oqo#t zZno}vN{Wm9sw7CNEkfNL$uI`gABSvdYYbHFBZ zh>#flH)J{dR!03k{6vxSYTsuuoe-H0ugF=HVFBf>gcO7pMOA^#tOE>J&Xu5-3QDE~ zg3oDpToXRPLbhF037EYD5D4tE&ywQQjNw2<0WI|L{FoCdO}+6Gk|U**x9*ktH&B@X z-A_q1EBjzFK7>d2LUg;ZVN(E*@sL)aZobn0B!sM{fRLL3j-uM|mPBM(Ae(6kjj8N{ z>g3p1Ob(-vUa?oNr96s}bWO&U*C&b7S)v7e43XgHWb#(X9A4Vf+aEbyAC8OiZJGEv|z+XKLGpyeNP2e zNPva{+Q`jfZxC1v?hkSQNb7EYcfYAw!}P1>o1qW}f^jb)>;JMM=<2e64Qkx!;-F@_P_G zwG?_)m+e40_A=zX!LQct{yd?~hrlkSDq9sRgr;(L{JZOmqyCzJ(9ZA2N{+^FY9;8o zztkDyzgVutWq-5aDgQd{ieks^4P8p3AKao2W+^o=)HIBxsD|d0(rN)>2ESIxJO;WI zbnEqKdLSbFkP!{{@Nn}HCO(BI86oM}|BB6I`V%H&=X|F;tG@Ex>PJ4P&5t!E*T^XOCR-oW? zuFKw2WPg0(ucb9@))m~mqr_os6)vTZNP1|3QaxE{EzOXzf#N&O76dzOs9sD$0ioDD zn8EJ~)fq)cryxD|Le&CQ?{F?BOlK%&$#YoBWj}ZBV!zINl3^c9QjI5;ET3bn#p>0N zEssr8p!dcvmH!z^Oxf%EpiYWX(Vq0dte+$`)k(7)=%pnPp>Ehh5P_xiLk6C`RDPBhyE@k5Ps7F7UF2D6er6aCtG}Rrd`_F-w0el8Wz$lqt52gV zS^^WQ3!`Fg`xDV7fT-R05NG~Ej`5>A9)F05)=D$0i_%TX9JDarV_G)Q&{G`=|5An^YWKgnHR?jAQDN^vj_sj zL_j2lBIBHQZm1UqS(0o9$eN#kv{wsJjO2TmwR#sED>9`V>yz7_TcbBSX3$5O)27hG zP6Cmlg#a-pF&i_h#4zncbc8*)j=C!#hte8UHjYNB8&w|2ha_ro+BxB7a&&LFhL}$} z%QXXO(~_bhP@m5opSGU`^BGw!J8OBDc2M)25?_oq+fC>F#R)XnXTOH}eNPgPdif#r z2S7bO>De+P12VB=p&Wr(MvAl`ImY8lO{39_SVx8MB?X}E6aHaA3=#9!TAhaPu^Qq4 z)ALVLoKVtUJ=qT6ZOXRA5Yme_&XTL*_IR2@-|!?09n4HpbbsQ_|5~fl$Y|_uBNY9N zU`+umsd~@6!NFnkx2bDRwe}+I7s~dk1exK7-1`67q!r@tZW%EXv@EOa2jCRv#{*fY+-A{#NQT#tzD>>E#Lu`IZ3p_@rhff~;Kld?ARXU^S&Kip7cc!xUeZ}|He=VI~Sq8CY4j0;j`h?`o zAS3tcEqY1zxb_)gPMWw>s{ghme=RihjoxDqgrr{Z*!2(;?Vivx+-2+&VA9_*8b^=MlH0HN_gubS!Zyl>DiqK#hZ-!>J|Q!PSH^wF;feyKBFpuk;n;d7+ezyKH~ zD)VWI?Zt0(KtGr5^Z50Zk7)<2bGCk(7Qg^i6BrAVJyZ&h#~~3AUb~<3k?bOxDt`FJngx;F6;X)CckDY2UVYBRl3Z9iNK82V$%2QX-P?frJH}VpHve%^D5jhnV ztQ27GVODePIy17xXOe4LI+@z{scWjblC->{&i@YuTN5(DMADVs%ZQH==-6ZNh!AGG z?aZi&3VOW2VR(11-SO20t9Y|5s>B&@jijVncja9|*wz&oE&4*uwIyO+bq;Bo_hc<3 z4aso?nQXb{#&ZuA0uZZfeVIS}#V8JF6p7R?jZ($azSjdzQevMy6yTP4D^R1IQOYX; zF)l4S&uoBn@X1j0QJW!!BZ)|LW6eEJU@Wvq>5KvoR34Hz4PBA1qGjr_ydT%b!%h7G zkkvX&of|7Rr9}u-iR1e=kUaLAh?lw=i658nK1(~|kaBHl=JWrqJ&=AcgwSxwpB-CN zls}|1RNE(!6oL#+?F+2z7iX&=%yE6Eh)uh= z|FeFR(M>K~Z>+0kQ)zDxH?t-T^u+-c3?y{nFiz=o4?(H6K*67gfPAkGl-5#!dBSw@ zNWb-?pW=9(XB8~#BjDXh{y`_cwP^_F(;pxNiqny41F29N_trpNLP3ga_g+7B1iI5=v#=U4a!B| z)ENDyryPEi1G>uF3LdOaB{($`E9{jG3E25_peA{97UX@C!#0DFN*BIGdIX)afG!Nm zk-nEgx;k0?0CXUh0RX@y9nXg4TC7`f`VfzL9v?Ew&{`qa{I-PjYvW%#>Z%r0u#}NG z)Mb+^S6n%L)TBnQHNQZa@Hq|T_PBlcV*@mWE5zS~5%q8CJ@`DGI>#U3Ka8F~xNX0N z=7xq)1!ajDgn9>u_EDp%GKh0kHj1$e#vI}#EPk)~PxOY7&Ny-N@{jczVSMp^y(aSL zc~TVc>NUAF1@gNvh#)B> z0AdTKQ@a%t1W+t=zIrFtsod&#f3!>eCjZO4YO@gfp4r+2(sekCItq-71mGkU=L%HR z%*b#;0!H!N^Aahi@JInt>N3DVvka0VD2b3_fx5**DA@UhSoeaEl7W4wdCEW%Kvzf+ zfls<__y-^<@6`?)m<5l3>-tR~0MeZ6L{j)crlAP4=nC{Spe(~A{@qK>ggz`~((lr1 zi5^)NlX&QiGB|j36KSMvgb1HX=&?rmWbls|(i>Pn3ht@0ftyPL_WB##SHt<VYXv4He9U0U8~pIMO}2MF2jU-S`KkPHAMi!)C~ zxp2B!5Ta9GR#eKS2v^4R%D3JI`XKQZ*;l++sUy_Rmx3KJyz}YuANv?3l?)N)Toj_^ z_^NDy=pr1bZT1X0GKam^Qv6O+uZe_J(KsdG#kWB&?_HF(=#m+&iVE}#~X3^2aa>rH)YL*+o$-R*>d&&m^IuW2_{YsajUQ!QOWbW)NP4*2Bb zb@vnDg(%ez&>~{5&J;e&%xgr<@i!5*M$!0`2jWKroFTOenSWp8j8XT0OhW}^i^2?4 ze%nCL&)~_l3eUAQ4IPY)zrVsNejc0cBxnerZ`b#LYo_x4?uj>(OxeCD)VlQ80xDt0 z?x*N_yzblvGtz41n_8vH%Q_2V>j&YLTB0H$5?0sK&_1+<~f~3$Kj}bl8(EC`1 zXheIz!s-Bg$SND{_D(C+Wdr@uMNc}h<{SLPm0hxe=r&fBBJe6bc0?iON@gK@*V{cF zW?;zTGyN`9yJL5uC(NtBo>HG2W$)ptmY%`AxD+5^)_iNsJ^hC%ARNdepndrA2cQ~) zGix4EF^4k5=iVl#Ys$tN zSS|5{xy_>CNS7IRq*ZsAs+N8r3fLXg#x~m;6`RIwVc}K84#9Fn0!J*tlz4~DD}ejk zU8r6F0oPJ=w28xji11#s#()&LA?=nFTQ3ZujbLHjus0+jkdQK5AVO}dKWuP zVgharjZT7Jd2k%2#9OX&dC7G5QJ+MVlR%~*Qj$6NZZQ z(qY4qvh*~PcP)GnWCfN!Y8GpLCOM$ zrm?b6{I~#{7-IqdgQ-?qZBf~(z!uW28v*|g?(0<()SWOLx2|3AFsiIhLb&JiKy-iH zWXf4Zx%~M1J;bKWBBy4=?OFQ;C2d}N!{Z|=5|^nIAg%fRMR?CoA^g&{k@ih6GqBV& zk#VE?WA501ZGyA}!g`S7d}-s18&m_uDb5R-w|&CBIAw zS&E#i9s4ALc+5?}hTkXQrU7^?aW3sxI--(X%z*0{81k7w9=7X5(M0I>h@f7e2@%Uj zV^q6$i(99p5YxKHy)@mh`xr^{N$noGy%Wf}A?$+$HTSnqjWlAj`~AFAFuZC*%dmVk zAd3tMoY#%=v86h5`*T@+egW3wz8ufDd7x0sx&ef>R7~2Ih^M=ktUcESH4ddm&da@f z8#=mj3+em$c=RrO!U#HwA78x-S9Fl}oOCa{@|gwIk<9{#aaCVe=7fBEce-R@ucY#J z`m;Q2tS}XPqGI*Xj5%^LL#SP?N;aV&R}bMwN3vLkKJ&T zdE56Sm7he1JawA?DtD@t927gE!COlv;{JXb{`fHSI#C6+t)R@2d9^|GxP;efAwcQW z=eUHY>cP4N%Yo z3>d0jOBeTfzo4|Vc9Cm-b!V@SzqwEsm*KH#tlJJf$l)xYHAY{ z*E-hhlXxwsNvq55Ncz2pxJJA2U;|S+Gs`H=wPtmtcV=7HpP4QHpiyD!v$sK(EvHzr zqzB!K;|~CrH!7zBmICE_ju?6g|Plnw$s&p?gH`41m}%7$b3 zqi=M$JMV93bko+)aHJrcYFMyz9LM9Rh%>#iqB{5w*Zj^RpQHNUg;d3f($bah3>=&>g2of zH>N~$WkqHmt1lL0DzoG=vJLG%(QWTN!gQu36I&U?Sii9cY0-jNqkC<5 zfMDe60aPkvS~U|3gWK6#fvo2qQ?WxQ{LkmgzoI4$L{ky^nP1}S?<=lNJ_uFDbf%zV zIPB>^o-);7AoZ}N%2_MS(y`*%*ESq7k+gOfZj$jCvg7lZ;tdfcGVbB6&7KnGa@{qm zfu_sueYEGFjF>)&dj_I>#$14Fl!OGF;i ztWLr{>*9IT+}G{K;98V{8*wA6r*PR%H#A$XdDwJ=oWqXXM%NO}Jj@=hS(}5=a@)B$9K?Niv4t~I{lXkkQ`%-Uv9_T9e4s>R2O-Lx8)A77Yk2{^?j0(FpNKQdQ>^O z*Vf-*e!Sk{cYFQywY3+Oz*hoJ&7e`lP4k>R*q>viYAZy|Ve~Ex+Xt#;{F6Ts8a(r%>D7S^DmLqTN&; zcnwjD0X;^M_g`MCZp8YuiFJ5J8;!q$3TQ+zeA%7*nY)7!hf~tdMP2(qBZyg3**Ke{ z`q1)@(32*n{;hoDW-gM~fa~Z(`){3Ulzr`K-;J30)A!AYY)zI3-T8FLJX02QdG0_> z%4*q8uieRce*g%R%zLSAlVuC2>$`+JZFLx!0Q@CCqJ=S9?T6E9i zeSd-zarGeCRMfs$sQHj}=gBjkpe%vsh)Y-}T69^D3j8$)T>-K@?G5Mm#yWF_+9*eQ z`~4?=8b&K-IwmK zFXlp{lVO(%zp~A=y~qq$HfxuzhheggeL7HFwxKEJCb4}^uu((=pra`aR&R{pu>b1m zD!u2dGv-jk!f9rSj(|z4115sx@7UsFs9Y)46T&Nr24bb=*=XC0G3;z!Gh1yUxI;B#8*NhD8`I<77bFPZMq{9k6+y(%q}LSE~YCqwcu}t-sshggj(AVN%IuJP*aF zfpDz(YH+GZK9=&bbBD;MEdI0B{R zP5gD_`^*rVgWX0g+Gidf)VeLSIjr(-JwEK>I(*r?w#jj#9fqig$F|CMl=W53r}VE# z|2aSyU4(FbOY4~hX74-Ow10=~QyR(A3QXUGp8fzJY}Ah`_-I_9_Tzfql}7lbYQKUy zrAs`HBYv`-P3fxfT_BSdUq#xpQZ390ZJPWFGzaN0c%nm3=^jVRm!wIFjKlwYl$o|pK z)b;^!(+kww!bTJH30t{CCw`3X4Jt%zr}6t7Moso}l?s7Q^$AMD+{Mx9ENkz&OsLOaspwZ5Wr9*-;uq(ue@FW5J;5+iktO0h z_?G*nJ)OEG8QV^hvfVK8Y;7%kFtmM% z_qYATqEq0T^~3RDka}8o1KMX6s4Te-tL%uDUbGJ}lMXl~S2^{ip)xP_#ri(5;?-h=$RLEsr(!NGLHT-swnE8a`*5d zm?64g z%Z4T0ReXu_`^^#hI{O)2jA!b)ig_8|AI!;+^?Pj>oxl~}SyHRFsWWt zH9BT+%dB4}l50hT@gw0D;nEF3BCM&+PPMHoyaRaI9AKE-W|KxRH{~<=Se*4uQW0*( zUJZ}Ob|Ffs+e`i+{2k69#GcO_oEIV`pAlY5WR`(n)_y{}ytKOXR@=5I*~bv4WO}|} zD0g%#dxM8s{_ym-(@K&ZtBrp`_v$Zzf9I=+z^&_09nNMoM^X#Ui@z?seela^qLT_< zNe&}seU197lYNp3d3R^vUe%a{so1m|>ts;t_0L>+UVbpTSkp1#KR7TQqFBe4**>}j zgHqQW(%;OLfBD9LOrRu&VYIGLqLH&oXs^75{E~RiUvS4BJXfWc=DtFm`7?@$xMm2V z%)Y@6MLmA;#&@khdU5#e6h2yC-dR-rvMEo?M~5zH?1hk{eyEbrTi@d-bS0Qwn_XIQ zMUQ@ZTovWina>?huvw%6VUA`P&e-d>2iHixQ$zDXA8z1i&lANDH5xZo?xiK=G9Hgu zaj70(+IS)rhVs**NtAut^@@|DQx3X=Z}fcGo6L!;Hi#*x6qx0g3g$qK8fObR>85f- z%65=tWw9!xp(l2qPO%?3!BloL@>Vn5gHi{KxP5Lf&I|KJQ*4_JO7z9n64wpmVEwXJ zxb|MNmpchx>KR!y;_Tv!OBV#I_v7^}P{7o2M)7wksE$sfZhHnt%S)lNN)oVjEE@_PL4OUzapQB&5SuLYS7qqcuiLz03Ih zDSg7YP4trwt8b`m81-RbIiVIno{nf#bEQQP)C=oqHbo_wk<#8ZMTUX^yZt?D7=Ktu zd#}vvhEb`L5rZ&v&Kvglj|Ba?%OUkds1Au*t8euAqf3*%CTm5%HRK^HnOah2LO)Mj z%(l5`c~p*7`~&dx4M}Mq6+cB2!!^HW^_`V7c<3|In6Q5jdzCos-N@_~v|Q@e((?Jx zb-yez)1h%v0WThU1O}eHOaCke5*m~7@@f-47iUOj+jnRWnWjW9USAy6Du?a!p$nRl z*qXfv9W=Cra$JJOCJ@FcMXdm>tp3%Bx$_#4^C{8BlYVpz4-N|I{OurPqjUKpgJ*_w z-8unlnHYR2hO?v~HhDK~M~i8xKv0$sxm}Q>Qeb5JqXrF^kYyz)^dh;Q3N99bd3&Dj z0$l?ci>J+lLYhxA(&`tNXE8%~9fsFJ33})S`dUWb>~h;VwD3)q_EFfjS`BjJHS>0t zL^x?1^tm%na6|RjwNZ$J^~LVbs%$wfYLlUqmGR-F?9Jef8C;GlM@POfS9AzH@rBA# z9=_Zf=B5c-gnj&GbRhS12tO)?nTcX5Q&5?H6#JCQSMP#i9G#ES=;>mS$m=Opk%PHH zeWyfo?OiG<#!fbFDc+>s*=-);u2%@--DyG<#SEc}OsKsk6GRMw7?)T|y9!E;p)_wW z=;_dHAyKZEUA-J~7g3|)HhY5T2#gtJTOOAIubdyfj)Z?DV2H?|B`JA0XZD%BAnPRx zO$bi}?YgQag*=RKba?v$>*o?4 zt<~mB%qBrfyW<$bH;%JKU)alf--}~A>wzn=jHD2PHp?E`=f9`mH zhorXpYPYyH+Mt^~!|?M%t6l(N18O4g9?1Rxuoe2JPDu>!=k2;_oV77snG)RGhiEr* zX8-fhUm;j#G)Llr57hN+tW{jv<{CO-gF63dT7RUIcQ8DktCY{X1Fw4+#C?3JY0Rax zlZB|RtWTJP)_puMAe+wrgfJuZu62GI>vw+_-l@iaFAkEAx%V=lAPqiol1?(GuY^0*mo44p5c%|j}zhqUKuD^#b}1QxWN zU3ZwOvvH5n+1Z<)M;RB7v8|Lw;$c$akxDggH#ErTV3Wj}FO4ys2!q?CXR>l)A5LXs zthA31nxE8|l{UN|FEYe6=nC_E!hn#ELg{ucai;7^4Ju+7b?2js!d0Yz36Hv(y4s^W ziQ_=-ow8f5A5GhhtRHQR>i~tuA-ioQBC#`NDs=g=UPc;DKd`h|pHddDF62axw>>Cp zAss==cOMg{GSuLX5AKc=i{mRjaS(axtIVD#6?0Av9kTSDQ2(|{bxvK(pBJw(O%gPQrIY`zb9^vwanH}ZUWpWVmc<}Cq|*^Hkh1&74yGLC9c zZ|zLb@o!{oZ}GsF{J2nwTE?2_*co|RRgp4#p<2;=niH=wUi<0NHoU=j?qaa-0JmKp z&D5CCE)X@iPpHf+PI!}(N741jtQ zK8|Qy?UYCkOKv=T+0y8Pls6fGw@ni6WR|_Pj=d!^JH)QI+g^vG%_eC9^ur`pLd(oDHHeCx?UM?s)Qy}TUuSrh@jqD#?ECwT*t}cpw=40xJMRt??NWG?YCAdKS#4P6fviFXF9gN zh%DH#Gf}WMX626bWfq(*#&;F|$c)Kxaux8CuTbQXARW7KLvFaaBmonpdezh5XW=Dg z8*X*Oe&T^#4JkAxcK^m>Z0G3rZXlY7-Jss0?+?$j)}R}mp+Pfv9YxEAPwB+wDSd;A zr|->Alt}I`wwxcN@iRlll8G|I_kTx9Wq$DdOAJktG1I2h3VljY62@!ZZ`ku%RZlPJ z|3Vu6+75_F_)ddk1bR#bxWZHY?mU#lx&qY`R8;fnHyDmE4L{$QiCNbTbMT{DSMGlc z-{MPQa9AX|;8t=8U2skpmtfRF&{$fd)G&_H!wAIfJw5uARjRxo>E|=(ow1 z#E@c|eq`*mg673K(mb-=&+ow`J-qN@&3;0epqM%uOw{3f2FbUB@Rh9s+&cdB&FVxi z+2ceIn0_lKWq;v>w1yDg_OzhA&zf$hTN#~i(}rO4l?F65JVSZp{r0|Q))#bUR>7uw z<_9%wrz1X3@)ZG+0V5S_@Q0xomqSzWs4ta~CoCJ5bFd1-`$-)|dchU;hA9X6Rd}-fNhzqD2@w>NZ<_;9{3-;SD=p z>Of4BCgMY8ilTgPQ(x!OE!siw92pE_H342Y{-yxfx7+q`cfpD#@JP-#N-xNNGQnMT zdwR80@R0_yW~Shk(1LnO@z`J7ahFYU%4M9aL`5@#Eva>!g5R{m%!~P<=onRToL%9$ z%7~kcy?VXP!zhLx;IiLPh^bP{^Ay04vAHE>@r}YmGfW;7g{*GsLaHBt22qjDzNe~B zwJNW7T9P%2XZ?I@f}6L5dr8pZv%jdurluXXVhn0nQ<^Lo%8HKAAJ}IezQ%>M5M`@e zqJ|SFP9Ak!>U9bNmGWV9shs@pcRnJgL4Dywf%M$wpYiy-KdfTd5e0v+mk@s-_5PI7 z?y_f_IAOU|Gb){hb!Y8MQZoFkZZLb@aING(lo!Z<$mrL3vi}?j!z9Tf7-qF}>KAAZ z=sIx?f`K)JMPi1mvEBu1U7fXIuZMknn-N62bacx)xFMbtJ)Yje?v?-;j-6r!vQ1^$-gh;U~aFgL11$;)1r^XEn)Z_1JejtUlA^4W#b3xBh*zf#>0dRbuC!>u!cBFjY4`f$ZT zrllyj&H~)DN1^W`U<%G;tOa{5e?+Xt!dogE{N4JSZJVtsUO zU=gq#Qu^m2`+`Z3&$ImVi0(~P>vxfxoyX((vP{}(ZLI7y zh;3dh*Dg^`W!3$|`(w-srUECqX{1S=Rd*?pUQt{AElRoeLhcAn6@@R`MCzo+4cCxA zwe^fkO?LQ__s^aYdZZN{Kj(g{{PBD0!WXvM!v~LMYdXqJ{k7ZfO8z-`3PRe^RNhTM z_}_^0D+gBW@T1s8D24yUAp3zv8~%KPfJ@ZI9&()cL!*TD$cavAL0wIKlI4j|F*Iqw za3jXb+rVx<%rN-31Z901TFrU-({G%BDWtwM{#JEDNi584rj1b=v>Hars#rlJHh{Ge z8cITvBqzy1QIa4zBOn<>vXTS@ z6hy^0rgP3c_uTuv^?ttfv3u=Z-Cb2(-9zutU4f@Z7|UG#Zbek8)ky^>1E^<&__;A3 zc0uXxefMAx5iAAF)04j+H4O8^n&U(nkvQl{J(9mWv~sVuAE zQmIpBWT1f0a}=_;gjoiZ86$W#5X ze3}#6m>1i7_L*)vl@IT_VSq!{GulP0!jo<@V{3hrL?43?*AUX4lTe~cAjndDKK$8jmXHI}R( z>i+$OtTjwf_ZdqEXe6<0sZM>E+%=a$ZbcpjV&d@!Vg~z3K}gE<(ylTGOJxr0eN`6F ztA0vq9x?{W=Kchg(y*i2ifLV~LB7L0+QyfXTN(gLto_x4lADI=1zLP#3BHckfIdRr zT5f%MbZnetURiXF`Ze`k1*=O6>%=fy<&=XT+6eOi919b{4)`9*4skRp`TuDW$8-+> zFcDk6{|QZK3Qa%z-qVpQXVu4l>vE}9Cvq>=l;I#mS(WIi|NhsSE;wHo6cg3r`$77z zLv&a)9t|DGcFP}}{8%J?{(Rwi&36P6o`u9G(*HV`g9ktOFmJzj)(7>(V7(yvj?nUx zZ^aLJzSI6pQheXB;ZolK)AJt?U)PuKbzMPj`+p$)0=@x%()}!w_|4|;sF>Zq|LLgF z{x_mu?EfdqZ@`EDL@NC!?N^}~58B*6^WT8U|0L~Sk^U2`_xHa47U4e;{8|3>KlW6t zkJ`VF*8R_f{hRAQv?lsX{GR`U^Q$WKSBq5tsmFhmAO1r_|HOar(Ga)55XZf9g}S9U z4E$*5Z~u0G^11Z(dyhOuB`7b(zyIE~S7d)Q_wPXRFGBeDsu7`oNBE=UF!7gQdOt{R z{384(X?tmZQXOc2uRR?2C!8PT2cDYWXDKGlXkNysi+qRHF!^UkqXa?|=6|&P9cb@_ z$tyFJ%SWv;KsleCB153;ols=b#3(xy8DjRMrQi5iP|3ptXkjPN5r-^4T0U~$GL)ny z2l;*}F#h<^?q8(YSO?P#!9bS+o!N7sIm{zm$FJ^AbWc-fI4>RT`oG3fb*h-+K;24K9C?SEZ(_xyu47|Li8T_mIPn7AXq6Z$Vd|2N=ps74IqMjAuN-F%dT z3B)CN=X;ah6R*5ah>kxKeb4;0-+vM0d)4Sq`F@`D|I+Kf(tL+^eoDEI2_bNX3Al%m z@2|c6J0T{RDuk{S6LsmhwhNxr1?T^3AO8+FLX_Cv?ulS{yx0DFvwvgkIm$hOA;+No zUrff8pAEhEZ?oWgqu&RpPx5GR>S&bPXjJm#C)q!rzxVw7#|ZEHegnSF{1}P@62Bo#=brp$no-H053S$l|AzUuAMQVAr9AH&Cc@jMmGi0%KkQrlH=d7o zBqfJlo$vE}I?_H}diI}r{x>H7OZC4a@V_JQza#L!Bk*$sF#773hygGr(j_hg>!-H* zpZej_ z-27u=vJlRRZ4*%eMr?MhLf=`gHVTE1Z1W{Q1i7DwonE@ftgu9b2CAX9b(Ldx}?a(OzA^y90bO-icm!Y zX#_F?&IHEnkFkU0jG5_rK5LOOc2t0#iqtKhwMt(FA?xq}AU3aQxcw>=5C^G+nQtq! z1B@JZ$LiuB1mP!q5N5|pT|HI64eFJRHFiczg;>=d#sRR<6>0+lv6p2$=KIUhVv>=E z!QAw~wHRA*V&FEE5Okd&wbvG19#t6zN+RKQtlsb=c3vmpi7wMZf}#LpWVmW`kA}Y~ zJWi+7a@jc2COP#K)+&dk!4f0FCuY=TlT4v)&k6#%O?y<4(Kc(>`sB#G zlMJ5s$ErWXbyz1H!+9k;4}=+x&l`8nZ0nismx8C9l^wjU_hBk0{)xr_&ws1sfEJDm zrV8T`CLSzI`v%zJT<{qjfR^x`O%|VA0uecKSR#^nl4n z19%nMLl2)(R>)_p5O+GDl_5DcB^r_RD0-*$ZiluEfB^P4$1aWR3?lN0q~hyG4_419 zh-P}ea9^y6J+)V0B!-5`F(9J;6_7vAk{f?`RoK($S8_W=M^RZFT~nj=+bHDh(4A=) zU(ldsXpxl!F~Ri(5vEvHET3Wr`8eO5lF&VoQ3=U68<7ay0v^XMQ#$)>{mIBCVKUR%c1IQz9(eikM6kP$l%=um% z5<#tka;0vakugQ3e=BJ1rA;6}1X^vQBoV9{RUwhQw9XFNV zoyq(dcD*hnoB^aw|IrNu6jU0NFPf+UaR?Oc)A`a;HyNb0wa9EGYE*`Fr(gV*Ct=Rc zbzIqZ>wS;=$3FW9p9Or{*~G(+GDZZLU1-vYQ8dZH6V5Dx5Rw5XNWf-@2cC)tPt1c_I~o;6XZAHNE|?wQg_g zQ{!l0blW9DJS;RpGEC>fF_g@IC1y&9YzEE~?4DertTtg;#SOC3A*Xn{Kfz2w;g#t^ zs@dt#sCgN$WJTx|>w~Zfw|^@F@UbQ`yiQDJ#fe;5ujD92E({aB#QQl1K7f8shn$dS ztk5;dd#?^HJO=_tSxzvZ!)6!5Q-9v3^|rSupL5jrB=wIiq8!AiR0=>p1i9CbX|u8{xup*tOD^u^r^G4m2k7i+~ceB_&`Mf^3PXO~N3r(qY5#IjXP0lbr}GYOg)fPNgd~JRRFOyEvug(`#7Su0)q^ z2oId?%9x~y-<7%Ks;1MfjIsFon60Dr0^Fwgp#wXn(>%OGd{w@zgxO9dxX4K38_-`A zs$vpq_7NRigw~jSs_7}?ToE=2zFbar=Wa(V0~;wx+|#EZ!z7U~u&{Wf68p{i9e($i zMn`rE1tV#5R8RtuQXa$1L(8=ow9TAX%<$(x6gRYMrxpz$j(d2H9l>am?zU#QNUi1%1ZzJmz`H_83`oc!8B?T#&zKcc;qCOC(Y=( zP1yaqKeAEyM5nXTgzT8z>fGLrUFD<{zk7!Pb&SDYQWvSG&xdthosA`1H6el+zzxU9 z0EZH9MAi9c?sE|$B|KE3_~BbP;C#;4D$)+Ptwz2MLT%v;=Ec|=2AuzqgjrxE&a z;JAli7@FCx&L8pRqn=Og1V5p$FXlnFI1(V8r4mhGcxb_tb0Gf!c3|USu;lNdU5pXr z)Lq{5)ajfbc>PO-0KhI#{5c#Q+NZUbYmdW{1EOEiEH}0Hz2Sl1GE4%|NM6K^=LNa8 za?}S83=s`ghrb&E(Rl!`@l*U|tz*f}o^HIX)kk!AGCdO$#PSDDRw_YjmtM);(LOl6 z(j+T0{+@!(52EM2Two59g3~2XfZ9nr4pDLM)oP*O0O%5-(-xjgR zQ1GAcX#g<;zoqQeWcT3wx{mRc1H^lzj1J%L_cZXY_p}@0D2vpJ4?#6kXU~nczruZ2 zy8q0HK{KBA+$gV`GTyHAHWaU*({~EF7qql2z~@mmPJe^0xJjH>W?P4@o=8r-AUH=J zx9%0jEOz8j8UkXdq^FJYtX5f8LgmwjNJG;}Q^{CB!5U2+btk2j7-OgbyCHAi0)&`a z`uK3b1y)HYj!&NDUB@s!T#z7h{bLE&^~m=7MFWMKYcZ^H>oHm~fpinp;(DNASH4$z zcvkDgwU1WsU}<2sd+>NfXywRKi9uN07dnbc7$MLx96Ri=RCXgJC|G?(2%I!fQ+KQO z>BSXgCWMD7DD3e7IUg}sNY4xWVBI}gj2RE{KvW&)vP4}m;r`T{Ph@})3?8M9W*)-> zoG#@lpt>KaX7JEhWIRH`N5cBt=58 zP{TSysod9{LP_m1G|~J3j%=uvZN{o&*pkfOP7k_LW@}l4A1P8U!*INbn%>zmB!_Cs;3fcVglJG@7hstXm{4i? z=pu}|k%2~nuAGmdPuMk2wx8Jve|z!XR10r~kA>lGTh(0P^+ySWz9_5--jU@?D#s$6 zoUXyMcYu^ihIr30frb08c+Y~pdvEa;U72}Fzq3W;eH__*CchZSu-BG!Vxe-oQVX>A zcu3^Rq_siAU8EpB{nne#BC6Hk+UtpbN3HpT2N)Pt-xs?R%oj!r4|86xV+t2#decei zaa-Zo`ei<9sq%Ejb!Nf&>5ue)dh`p=J&TzguX9TEnw<~Lckqj6zOcpA?l+f3bIUWX zL{itYR+=pWL{<}O&m1)9Ec?w7M{1&kK9PJ2c}Nf`_whiyfUwPB6a)7#qs^$>g|ZR+ z@VGTkXY!cw=Y3TZr)E6QYo@n;H-ab?Xf{&YGUF=ts=oX3AHaCoJ7@NkZWhUgunuD` zY*ZzA2qC;fba|X8TZ4_Nd)BxQ!Q`0^*Wv=)?W-ZH{qk|o+Qp?ISK+G?GKduR{kqI@ zwn0LYW5fB*+GSTHMU}P54s&44X@A8JUbPsux)aZ}SIEG0Edq6hoIS=B-&(Cg(HS&17;Z0Kukd!Mn)(CGiNm7~Fb%;e|5-6+=ORh|qmmdu_dpDI zjJy5=dGk}oS1HiYvJxL;^I>f|GoLNDv3w&{QyLh1l>4&JSOzv<8*Qh;)JI^pGL?jI z{GCp>3Y>}-m#u6>l#q&53vVHuIC~lC)7jD$BF9bIe-oqmu#10Jh&I^$LN7ULWssOA z-0*feT|N~hGuZ3XYSy_ZoAlt*iQ#j}t6xMP@}C~y9-rd!qCuQwbPN;`>12@gC@5Pj zsLt1}|3b-e2l6(>t(L|?1E-DG@teArwA%@zAL#&e?FpsqTg-@I0?lFkYd{o@A__bw ziY0+mh|8jk%!yNvVHZER!q^z*^ud&e$&cBWSqNEM)#_P#f5-mb-XLIuq5ECneDG|QVI=yWDH znstw*Or>Wm0e)h0?K*{8Eo+`EXiGR%e+oZP`*{KD&d(7M!latH6RS%S@>`Tol7=iz(jlr5+Foijn92Oh~?29Fj} z{(>6R^E`2U%ljKZ-=NrYC;s|ZDLK(kEP_GSDXN=aRGIe&7%xZ{YR!k-#r$6u!qY|jg5u7DLdR?~RySPc>?EUl){5nGKjbNGze4sr5)QPa4kcq}sMnX6yCKrs-Xv=8XQ)5Jw z9AdbyC51!m5F7JVA*{y1Ve-!=B}p{)-AX_nYf84kqTn}FyE1l}N_OmRA84>>P(F($ zj0|C(um}_rqiT1(fWL>o3Xb&^R@(NeO#d`?5A$R0Y**V0fXS~RU(F+Yudz6soSksZ>z@LEdUDk zB!1%h)%`Z@JU$>=c!|Xjmo!Q;vdTtM0r_d~m?*R1EFhd7FdF=%weMxEEWDnP17Kvl zLaLwk;zTZKf+|_m%09Csx{Fj#BKt9p*;f-FaF~8v$vX<12EMr5vj|1SqA@tK_tq*7I$BI8F(sY1b3K0^TFZZ<2wr0uQ98;0F)@WAnL)1Pp8JsWW7EL>N*zX3GGGs|~x5;tYhwUFsv z%A}1`4<3H3Qgo%`{r$nP%sQMx`+I0oyJFhL3lyt2_167ak3qIz6xA^vhz7?N-t$JjjxC_P;=GFQyGgGL)yG-h3W|S=pKj= zHNEC9hFK2mH|ecKpI=t5IsMSgL+gQ=LTslmTFP2<|^U_JT}Ja?;o zw-x21;o>8sgB|%VWq(DscGvj^^dw+hRN{OTsc9-s0`)Zt+rMcSXXGsR3d(<~5wqh0 zk7zX5cFpSB!{XHC{{zOQa9P(@~m zW3p*j2#?R3aUldmYMxqVOG~PgCGctI;gKRnRUXg4Pw&vhetMxIaYA^RfnBv@)>((k zBZUMwDl(R8E(u+-DIRw|!+!emz^KoFumF6F9}h4Xqek=Fm&Xq$9+>y;j)V8fI+!=^ zy7uvvAFHDn=O5o!N0;~6U$;1ms37*s_AdU#eEwu@w83ew6I~MVDre=lP5lqk`h3Yn z7>kKj-IXh~-_^ciS4izt;?`%3%`@Gz=n zI9YPikxIUJpSL0<=626&=|%E@FQJwQvNIBWr#K^z_nGsJW5gVRz7LgOkg%fPj!Ws_ zI;~bfaK}DSAl~gi!&~=g>WqJxbUl7`$9Si z@eT5Yeg!w!o)!I=`JrZkIQ~OIb4AwG)$I6{K6z$SGT3siDQ6)fkMjWAjZ%ygp}+~z zd8Ku#XyX`~)RXO9sTqQ`(_U)2SrS&ID?b$9OJJ)LY36u&Ij3gbZtL}!&Mw6o?tB27$lBTZ5Nb^y?m~*3_hp%Z@4 zHFy<132=RXf#v3a&`r@gVxiaq=PijK1)N219u3mz$C`4NXw+%izp%r08WiVR!BiV7c6REnu`ux=f~#NG0R%>-|K zfFbQekQ!OkeDBnZZkjh52ZMXA6P{6Su9kpw5YdmKT6wPU*&Zq|X;R?#zS2pA?S&PT zFPJo5&{1Uugs6o@@M0Q=r1h=>=_hnwoxcI8B9>U{{jm>6NeAMS$px);9VYxL60bSr zp)(Xo&>c^z7Lr&3JK&pn3V5ei8$Gsesu7ovS}PEcn{j4_+{^n0@Yv8hY4{XV6whO+ zv6638mE+dpgjsj=%d|-VvdG$ek))~NNdvXmcEWp8GEg+OQHfNi-k6-;MAb`pq>cO1 znY7CV{tVd+D(%!Pn$!F4dBnsx{UvqJ*(IyK0c&3@6s*rIG5#{{rSaXPX8{u^au7N6 zCDKd!y_JAiiwr3pru?nM5PHynALc%AMxZ?0p=6Moz`^xX%rD`gb9*Ra4S_6 z6NS?3?);4;{SXsFFC=L8gdh#9-D!P9aXGsa*N`;#{Gh_tO4QF7c4P=z+HH*VZ3pQF z#NSPX^iZ~KFqXetkAE<)ajhC%ijBx4*r9*;(v=2!D^0;{Kie{ES_NCDnC^(l9C!A; z<~Z?Spq6Ph3whp@8#S*M%QV~!5$*QS^XdxiNTm>jj+*pHOg1%KNT#dd&SH>u`$cKK z&w68oM55wb%GbucR6SR3z{gBX3^N(mqMXV%`C!x^4hK!&rMyWX>o(C$ z?rL0Xqs~hrJl((GAV0@o$3(}|riD!~F(%D=9s%Q|nhCSGMp#amQ8MGf-0Zw-|K^cW zDuYU-LvQt2B<8W1di0Jyv{%pJ8dqTBU$+)mqyu9mQWzPE8;E35250oK?DVBF7(lgF zuQ%mXP4=?_DWl`pp5Y5e=t24iKe%rR*s< z45u8~b%Ef(xj=!eO=h*!OSpvD-9d*6VmJkrSJ0<>s-WQ0*&D~`q{abAFYj7F><7r8 z?GW_U5Z$};36ZYgeGN*`vs(gm-5+s3BdJ!RkDJqX;0yO}v4EtqK@YwGoC;;TEM$?g z;U~yH9(q5XeeHQJpGSGT*#b<*xXD%$qQ^y0pBe<;o(bW*NuXwQ@EUn+Z#pEGL|)yS zO@@wGhTue3M`nvByKvwhy>HDvsa|M&;JrxaS;yP84i*14;EkHvM~gw zuaD_2IQe}8a=yE@!1$>6A1~+qx#FaX>9lv=usooEoVLpZe0k8yiS1*yd+}i}mw4bw z?kPM}x(RITeVlqQVPtxpbR+$Wvfg6#&LuLV9OdEsp8ASc zkz<+Q8}&^e{e{yMcaxl{qdt@hC!1jc%yk&ovj_=X7AGUm?0YkePu&I8zbxBD9doJa z!9%}0Xal5DYWX(smCI{6#UaC?t=wD9fachpkZft@g!DaN+96Z>uaH~$!*o=S{VR8O*{J}*-amMdK0P~arl zx~T9N9P)-WD|^Z8hXv1$Us^e&AxBb@{KV)_uU8%$vES`}sCL;^KylA#D*G{B7Pnp- zf4b=Q+iv)W6){GQOtsLh6x+b|)#rWmLgT}#DsJK2%5%XFxxGP;Pe-TkTsRwDgI)_x zXw?eoL6BZGmm;mr3S^F9q#>>T6G8gg7rPzlxUY8a|Bd#)R8dyT=-ncM<|g%6OI)&% zug}5xf8)r949WB<+K|FiHNTRi=tIifdA|qW{&gBMrL5v=_3B3odX$*wXtSgatU+O` zz(MP5jVntmov$h~@vEfN?&rE|t53*^F5CzmxoP~|19v2Z-7HyO$BXD;vp}qjz464G z;$F8M5nmKCKnSIlnCL5K)VZ9yovHD4@&ezud3)X-?67r01t}v#>3j19!$@^dTk7GsL=czcm9Vq>*ou7;TdJIO!^Ts$kIhtvYgmoSXW|v%709P^2X;Rc>!|ZXSRG zAo0m`?fB~glA05#k_42{$W1;L$X42^V6sf zbxdU5!`o!+2AQx!E5Sx~dXGkY+nl=F!}-OQ^B(ZaS0}xzlQ*q|k2)uGukejT6x_R| zjL;$W9vJA1V4ivLKq@#G`L;OUfA0pZGpOBklx@M;{b;^zV{T4tU87~i{H0jUIMfGnq)JtE^ zqHRto@ogsZ`d8Nh8$)+9`uRDoDo#xG^*bYdwyPdU9)HaG?K#h5ovC(b0+p)i;U26G zev&>9K+1v3#v-+^E$VI;T?#w)FsPnPLb9=yRUD||644+zpjygK&rHg8nRlZb3Y^rK zEo}fj*($*6MBaYR|Lt8Uc{`6G?aM`ENoYXgO;xc#-<>d2vQkswbQD%ZI~z^gqYz;6 zbqz<yI0JIA5gftoh@(XhS*}~i_Yd@vyo3g z%gQ$$R*l->>NEP{o8~|sx^O7T6M(BF{ zt_2Lgohht!Slc7WZZ+oLe5h=b&+quLhl1amiZX0nPWPpL-s3#dBcD@ZE5=NPw@3b8 zUf;Q#JF+UYDsvY%L$)t#^v%(yXXmcc5B_g}c2gzTSd$KV!=3I{rNIa8+>u`#gTodL zu9(MhmfHPp;YWqRZqBMF8@3@t5Xy!{=0r`FMr8S4TWSXp(?!qBGD2Un-v24-A0qz% zU>Yc#=kIYh+Vz=AL1uv0Hz4?@Z-jrHmcMeVFGy}sadL}P%uIa;sqN(L0K-H|q?Z9*`(R(}Kb`1Dx-@qG33oJGAhX}kCo z%WaCB4yg?Kw+bj0;U`?;QmbX7ikW17IQi`3t#~g!JU{Ig)v`-1!A8(^3cz+`42)4THK!D zJQ0dh;Y0WuQTjb%1UksE6ydKr>rG_~$go+)IH?6FPo0kWw6*`?-&njfl3AmRYJKRD%chMn=r zPX!vxvNLuv6fg+$v=Y$rZPb>mer4!)07c##W81M{MR9?G?$;tN+R%5x-kVuDOj_)F zA zT^?0x5bG&;6XN}r5*1ZB@8x2OYto_udCDi2=OZekH}DQtZrOw_;GP3-3ux|~lwA8d z8jNFN0&pUoTD5a_5Y{mMijs=~_GI9g7MUbNE7XNlO7^9YFzlW;PlxYjhC8CYQ?Zpp zx;f+7oJ|aO8TZdmMU73Etc$;9%&gI{2coeh|>G1M=a1U(M(V%t4ht zMYDI7gf^3fK**Z#YFW4O9y)09Wjfo`-l~NH9PSC@yVS_-vA6eA;$9}IJ_^^bm>BOG zyCFmy#W=qy>^(VT^=zzgIQZ~rNc%YFJXhc0R{1@#s(aVqda2Rs!Qihu?pt!nvKYmk zFNI%PB{RmzH0(TZ3ODEq>&*-}CL{7kKtKq-fQ!|q87X$9GluX#%UCtq@!RO9^p=vj z(`%iUV<_66tQ=r|Z&tgW=uM}HK}wMRf{M{qZ>Ujo$i>!Cw>4Ne)4~|y zzLllC4#&!kFMYm^EW8?mkgM|@A#%K|!t!{ij`?|Y3#N2itsse)4s#n&?om#NN;UsO zAC1rK=fy=%3L{;?2h+#G?sP62vHg$}iYUgZrNYQM{=4c8pn9~towGzEW8;O(via+A z-D0B)nN$f$hJ{g6VY}2On_OC#Bfs3PO%YTHQJgADadM_jIqT6JreRtPOU_gv;VIS7 zvbT(N8YBW!?iApC0wrInA@xg=VNwqJ21MXAAA=Nykvh~(xBSG%J3a&77d9!1l)k)$ z?Ni1{5@dh0X38Ck_l<={J-;{B5mxm@{0hRQw{2da(A+)j@`J8O^d~sih{dJ*>ryvh zi+&LZnol*dx<#`4+sPPCncRx_oP(KlNHiLAdyT?Iz!>C1v`4=d z%R=$?Tx@hzFL}I<{_70@-*SPOJizt6C{84A6E0T~_)NOE@glX}kvD?qAkHBpM12ne5 zrIz_9}e8vlm9`p(gm(lk=M$%vwcK@LMGICUG#u(GH6p3 zF+9|H{Mfj~0Vgt$4M`F)UF(&M-|GZ1W?ZuNz691Y?XlwIpK-@AUq*QqRB91v5HjDH%I_B5$}Gx# z-l*;ni}brWBfl-a^*q>7ga*r&a6kJ!*10N)_UiZTrBWJ8N3$tEjlTU?&rCKnFVZ*v zFcBwkV!3M%lphea5B+YXzu*isiQ|96tZ=>7C=B*uEC*Lst{O4!C%vYy*SID^?`q~f zoppUt{6=u?r~^^eJVyhnMw|skF+DK*uDFsUm%SSY40Da@!DjPq?GVohXH0{Ktt5GM zgAm2|T6`KP<7hQTC9OUyC@a3uOTs)jj=?N$UaJ~{FrC!eG7}DZ#-!T;b+x`o*cvkj-SgD^C1mnZAtIM$^{vp2qneh#yDFc=Bawflfd3u zEc9R}U8OCoWt>hxCR8p{k&J|9Tpi7kyk~=mX)&`5hSNcW^>J|93BrJ2oN%ESc0Ltq z>}@YDqdY!iULAg7L}=wkaV6}|p!p-+lCnIZrJ z&|ZuZ4y%21fZbD6%iN*trW8fzCV5Yk+mgN+ocCg#{$LD&h3MwH4*CWl%qa6XG2USs zOp&r_to%yh6x`g!v{9|4&l=f6?x(;fjcl)8~a$VT1O<1Tz((Nu88Mr*6!wL6`td}{u3{FM8=oXBl zlhH#>W?cIy&4Yo}OZ-+Y>J0N)Jx6B0=F4j~KKHVioN7IaR1-~8E?_xs{2_j2BAFoK zgX$puB1&NvOrr)@qq1`iE(aDz-tm%$iE3aT*W6+KrtvySa`0oj$dyme0C!;?kNbz#Q#S;fkIBa2?@& zN<$yF^ufE`o=}!WFyxIZ_H0%zldM%jn)zw3vsZC9XSu9{4YLrrqC8I4Cj~Ix5PBVx0+Vs6`T79{a3Ch%5eVpb2i z9%jt(@jchRhf`2UiT)tfaqRVZzf`i&?tJ^D+Hzsjjyh)DqfV`kcaF*}sOXXedo_!x z{*6IGD*MaP(Q-DSM_Pi#c!|gC&pdAP+t9-x#u1ohMmf9m{IUP~X02)G&n%6Y9j!fra`weMQPiVqerg|@!lMo9Hg+pQY%lEJHYAZbF z;8RnXJ6zHBGraz&&NRw8u;hUi@w~+^=lr^a3Auq|&VB zOWJVvzgvWxlKa@c0o83c;zYm#(~PFSFWxwc3+ob~u*uZNPcPWXJ^%3^y!!s%q2<)- zZDzh=pcKFU3S~F_!>p7;-zz=;vR2nnk*yR7i@K`h&?$H`s{n>(VOc6T9f_J{xujLN z^J7V`oET?nJnez*=Eo5qcf&=xmNmO{g0 z^8?1>_d<~GnlUa&qPf7qMoPYhE&<~bKJCni0=H#!IpcPUw}mch+Z#%bG$5*}#2JWz z%`H4z;J`KD!c~5r#|e)>))=(5hWzkf9G953rR>`Fyy4Mr7p&iaaiWLGSe-iuj$M_y zxK1fggBwcRjMM7vPgu@ZqhOs9KJNoVGZ9wAqxvK@DWc99_!GTpMU%8vUBx7fuOOlK zgdI#$avn&*$~ukB5|Uv3;aq65I?;@%^qRYRVYOU?LJT4?pz7qC1cG)GPrb=#w-yla zt~dgJ?hlj#=iGR{YhrF&AUv8^r{TBSNg@yHLm|pv$p$Ow@^JIlZpPQIFBs2899`n9 z5rtyChEk|jaPLIuXqSx)mK*4omzS>;d2{3|I=0QCw}=M>Vpd?mp7J9qp3~wHZ}yH- zJ&cexX;Qcsvybd>=Z`vi%}WTtib%3@r!KtyNxWlLv#gzq_?wD# z&$*4S%HNB`NeY>89m3mEeAbv{SYAMXh1Wd811cYnt(JCS4C9&j{k}ks9lIL5%wtAA z1~#NZ=yj3obt^I71-g65eqtZ7e(Z}KtuRvGx5Rz)Ym4mSRVIJ-gfzG3pUi@K&Td?p zr8UJ@ajfjvjZ*2AMG#kP=4_DF)kst2E2_z+Uc6ySA)rR3`8w%2o@n@+CS$Ku&9R9E5yJwGQe|l@53oW*7x`sgT6Z3c1T5~%z7&*LsF;_`k2uB~e{$lj}whE`r7UZzgXpY*Zmn%qo7U5&~WeAUd-GWJPu zYVnD6Q)RpWc67bdv(K1S_vZcF>UXD#ws$$BI_~Dk$9)3=b}vtF4cKf%>j@W6heRex z48=~ncWoRnsf5e6%TTm_eXdXm;D{U5zP0d#x|LrOp~JZ{L0Ng4xh(WRJs^P|v#UZT z4kqyvL!}WKt3AdpynpL6CPKllhOD!3T0})MxhZ{}+kLetR0KuR=I5Vi2%YL&A!~3j zDX)mB=QG3>FW(PZ8RszIA3G8#b!fG&7Lxl=LrbJ!sjFDyz53t+a~gnw-ZMLM>R9%g zxy~+qoeB8r>)KJlu&e|*JTrMFnH}`LmDzXzNmV&Jc+ZK*zCe}qK`-6|vJFMZiYZ?t zUKtcRX@C+opspzAu%VX0r>=r>LO2x#1aL&tnjgsk9$u*8SdVN=QECC=$9#KNjnGhN zSBQukB}av9$QVOC;@(Mvfg}TO>y2v^j+4cn)AC76$QYIpjxk{}bD)x% z1OhpWdd5`MPHQl0`DB+uC|uhRvqI4FcBdtX;2KsqYcINo!0TpKI&7qNe)CaXr^{)yD0<91&1e4A;~?r% zen#Xj+A5MDL)~_mT+f9&IYX|big{0_z3fm=UD=lY@$9kMwe%HhV%EW{9Cv;11{J3^ zr#|L%wjc8Vy^>{yQ=(c!8T#3|auTMSLxsr4NSaqgwHBv66cP|l>ZQ@rcu1!t`-l8l zudUW2`q`VKVk%-{W!URvRul;uzX8l~Em(Tn*)sr3$MqZ)K4OPnYUfKWScCaAjGAW+ z7(4tWX|sj*Aupm`&v)8_S{l}{vq^sc&fIkBM2-Mw$PjL#9NEioT?gkYUweP-?ETu{ z3kF3Z!$imQ%Ve|RVCz5cG9Uc3bS8>D|W8Ron6 zx%$)D-cra*Bd5(df67qFVRKtvLCX8F!#!`Tj`;mEp8b2lb^SJe1+T*J5wPJ0169z7RV z(Tj5U$cF*y@v0s=+hG*OqjE}Rb`Yf^&}Jeb9XG297L0g(e-!hGX zAD?lP%p?qApS5E5U^)%U#6{3NSf}!H37%G{3;x6lcE`I2LW2k)9XA@C8^_uwV^k@5 z`t$8)&!K}Ua&DZOMf-JqY@9(Kp0eX(Qw!qTR(svnE8YisNNFRYLZOsZE&Up~WEq_z zaP_C7y3FpR)J*wJvB5j2mU55e$@nZBN|c=tSujB}d?JO5v@A@9CcXXv;Dic0hG9xUk^P_={j6B`# zf;=nPS@BHqvRXg2O|04~*5f`S#t@??2`dzYxX;-VAJb5co(;vR0QaU6x9CBXSX*G3`r=QsggP=yMVr zLVD}rS^}@bnmgzSUM*3!Spjd{Ip@^4Uv)T0ILL6ThdHfTkcqv(SILC3j$%8Al8aRH z&!HMfpVE7>udpj7=`7c5HyLYneogeUEK^n#!g`fB7xGGLRh>0vJ{PA% z_wu~=y|ViCc1l1NimO^!bvh&mHe{`wP@(p++U6gg+-WCf1O(CsoL21FtaViRON>P} z&?%dhPf7g)j)wYBiN5}-DhJWjADUbj7iYf;)&g$tOXFW zazQxdD4NV%#*tnD#dNXwaX1@*u$s>Id=w?|!hK?~Q-oPV+iXBQft8@_kyvG}Ol`W! z7kMzR3upCF<=6m@-`Jy)ORw?vhVAif1MRZ`H`FSIQvKTc<__t_Iimq?KFxj`c{{w*qjIjU! diff --git a/docs/user-manual/en/images/architecture2.jpg b/docs/user-manual/en/images/architecture2.jpg index 391c1c01b3bbd265600c9b03dcca7fa6034ed615..cf30eeb3c187e06c3c27ef79fb74958a1049935b 100644 GIT binary patch literal 27525 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 diff --git a/docs/user-manual/en/images/architecture3.jpg b/docs/user-manual/en/images/architecture3.jpg index 7dccab73ed3ecee43dbaced89d8d9ef1f1fa43cf..8a45d0bcd037c97383a3cc3973615bca8e231c5c 100644 GIT binary patch literal 15699 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 literal 14069 zcmeHNc|26#`@b``8AfJC*=g)#-?Emm??Plrj9n|DtYytk$gYrG3Pok#DN9*GDP@cB zk-bPVzpTP5fKp-3L%E=C54fYz^IYrWP9mp_A}7a(9xk-c-c`*+{|=z9HN}u z`~t$l!u#394~Yp%@d^nGu7iM}P$-N9Mny_WCCEs}DEP|>TL&N@_!f9HcpxSKi~!*w zK-dO=2B+#q0=5%A0U;iEy^m^V4{lC)V0`RL0EPzwz;HY`jxkfSJ@6kR#7(kw-<^

O^m;s%FsB@ZHKltGXk4=NyP6 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 } ``` -Likewise for MQTT protocol, an interceptor must implement the interface `MQTTInterceptor`: +Likewise for MQTT protocol, an interceptor must implement the interface +`MQTTInterceptor`: -```java -package org.apache.activemq.artemis.core.protocol.mqtt; +```java package org.apache.activemq.artemis.core.protocol.mqtt; public interface MQTTInterceptor extends BaseInterceptor { @@ -46,16 +47,14 @@ public interface MQTTInterceptor extends BaseInterceptor The returned boolean value is important: -- if `true` is returned, the process continues normally +- if `true` is returned, the process continues normally -- if `false` is returned, the process is aborted, no other - interceptors will be called and the packet will not be processed - further by the server. +- if `false` is returned, the process is aborted, no other interceptors will be + called and the packet will not be processed further by the server. ## Configuring The Interceptors -Both incoming and outgoing interceptors are configured in -`broker.xml`: +Both incoming and outgoing interceptors are configured in `broker.xml`: ```xml @@ -69,39 +68,41 @@ Both incoming and outgoing interceptors are configured in ``` -See the documentation on [adding runtime dependencies](using-server.md) to +See the documentation on [adding runtime dependencies](using-server.md) to understand how to make your interceptor available to the broker. ## Interceptors on the Client Side -The interceptors can also be run on the Apache ActiveMQ Artemit client side to intercept packets -either sent by the client to the server or by the server to the client. -This is done by adding the interceptor to the `ServerLocator` with the -`addIncomingInterceptor(Interceptor)` or +The interceptors can also be run on the Apache ActiveMQ Artemit client side to +intercept packets either sent by the client to the server or by the server to +the client. This is done by adding the interceptor to the `ServerLocator` with +the `addIncomingInterceptor(Interceptor)` or `addOutgoingInterceptor(Interceptor)` methods. -As noted above, if an interceptor returns `false` then the sending of -the packet is aborted which means that no other interceptors are be -called and the packet is not be processed further by the client. -Typically this process happens transparently to the client (i.e. it has -no idea if a packet was aborted or not). However, in the case of an -outgoing packet that is sent in a `blocking` fashion a -`ActiveMQException` will be thrown to the caller. The exception is -thrown because blocking sends provide reliability and it is considered -an error for them not to succeed. `Blocking` sends occurs when, for +As noted above, if an interceptor returns `false` then the sending of the +packet is aborted which means that no other interceptors are be called and the +packet is not be processed further by the client. Typically this process +happens transparently to the client (i.e. it has no idea if a packet was +aborted or not). However, in the case of an outgoing packet that is sent in a +`blocking` fashion a `ActiveMQException` will be thrown to the caller. The +exception is thrown because blocking sends provide reliability and it is +considered an error for them not to succeed. `Blocking` sends occurs when, for example, an application invokes `setBlockOnNonDurableSend(true)` or -`setBlockOnDurableSend(true)` on its `ServerLocator` or if an -application is using a JMS connection factory retrieved from JNDI that -has either `block-on-durable-send` or `block-on-non-durable-send` set to -`true`. Blocking is also used for packets dealing with transactions -(e.g. commit, roll-back, etc.). The `ActiveMQException` thrown will -contain the name of the interceptor that returned false. +`setBlockOnDurableSend(true)` on its `ServerLocator` or if an application is +using a JMS connection factory retrieved from JNDI that has either +`block-on-durable-send` or `block-on-non-durable-send` set to `true`. Blocking +is also used for packets dealing with transactions (e.g. commit, roll-back, +etc.). The `ActiveMQException` thrown will contain the name of the interceptor +that returned false. + +As on the server, the client interceptor classes (and their dependencies) must +be added to the classpath to be properly instantiated and invoked. -As on the server, the client interceptor classes (and their -dependencies) must be added to the classpath to be properly instantiated -and invoked. +## Examples -## Example +See the following examples which show how to use interceptors: -See [the examples chapter](examples.md) for an example which shows how to use interceptors to add -properties to a message on the server. +- [Interceptor](examples.md#interceptor) +- [Interceptor AMQP](examples.md#interceptor-amqp) +- [Interceptor Client](examples.md#interceptor-client) +- [Interceptor MQTT](examples.md#interceptor-mqtt) diff --git a/docs/user-manual/en/jms-bridge.md b/docs/user-manual/en/jms-bridge.md index f859e7b8c71..327d37f0e84 100644 --- a/docs/user-manual/en/jms-bridge.md +++ b/docs/user-manual/en/jms-bridge.md @@ -2,237 +2,222 @@ Apache ActiveMQ Artemis includes a fully functional JMS message bridge. -The function of the bridge is to consume messages from a source queue or -topic, and send them to a target queue or topic, typically on a -different server. +The function of the bridge is to consume messages from a source queue or topic, +and send them to a target queue or topic, typically on a different server. -> *Notice:* -> The JMS Bridge is not intended as a replacement for transformation and more expert systems such as Camel. -> The JMS Bridge may be useful for fast transfers as this chapter covers, but keep in mind that more complex scenarios requiring transformations will require you to use a more advanced transformation system that will play on use cases that will go beyond Apache ActiveMQ Artemis. - -The source and target servers do not have to be in the same cluster -which makes bridging suitable for reliably sending messages from one -cluster to another, for instance across a WAN, and where the connection -may be unreliable. - -A bridge can be deployed as a standalone application, with Apache ActiveMQ Artemis -standalone server or inside a JBoss AS instance. The source and the -target can be located in the same virtual machine or another one. - -The bridge can also be used to bridge messages from other non Apache ActiveMQ Artemis -JMS servers, as long as they are JMS 1.1 compliant. - -> **Note** +> **Note:** > -> Do not confuse a JMS bridge with a core bridge. A JMS bridge can be -> used to bridge any two JMS 1.1 compliant JMS providers and uses the -> JMS API. A core bridge (described in [Core Bridges](core-bridges.md)) is used to bridge any two -> Apache ActiveMQ Artemis instances and uses the core API. Always use a core bridge if -> you can in preference to a JMS bridge. The core bridge will typically -> provide better performance than a JMS bridge. Also the core bridge can -> provide *once and only once* delivery guarantees without using XA. - -The bridge has built-in resilience to failure so if the source or target -server connection is lost, e.g. due to network failure, the bridge will -retry connecting to the source and/or target until they come back -online. When it comes back online it will resume operation as normal. - -The bridge can be configured with an optional JMS selector, so it will -only consume messages matching that JMS selector - -It can be configured to consume from a queue or a topic. When it -consumes from a topic it can be configured to consume using a non -durable or durable subscription - -Typically, the bridge is deployed by the JBoss Micro Container via a -beans configuration file. This would typically be deployed inside the -JBoss Application Server and the following example shows an example of a -beans file that bridges 2 destinations which are actually on the same -server. - -The JMS Bridge is a simple POJO so can be deployed with most frameworks, -simply instantiate the `org.apache.activemq.artemis.api.jms.bridge.impl.JMSBridgeImpl` +> The JMS Bridge is not intended as a replacement for transformation and more +> expert systems such as Camel. The JMS Bridge may be useful for fast +> transfers as this chapter covers, but keep in mind that more complex +> scenarios requiring transformations will require you to use a more advanced +> transformation system that will play on use cases that will go beyond Apache +> ActiveMQ Artemis. + +The source and target servers do not have to be in the same cluster which makes +bridging suitable for reliably sending messages from one cluster to another, +for instance across a WAN, and where the connection may be unreliable. + +A bridge can be deployed as a standalone application or as a web application +managed by the embedded Jetty instance bootstrapped with Apache ActiveMQ +Artemis. The source and the target can be located in the same virtual machine +or another one. + +The bridge can also be used to bridge messages from other non Apache ActiveMQ +Artemis JMS servers, as long as they are JMS 1.1 compliant. + +> **Note:** +> +> Do not confuse a JMS bridge with a core bridge. A JMS bridge can be used to +> bridge any two JMS 1.1 compliant JMS providers and uses the JMS API. A [core +> bridge](core-bridges.md)) is used to bridge any two Apache ActiveMQ Artemis +> instances and uses the core API. Always use a core bridge if you can in +> preference to a JMS bridge. The core bridge will typically provide better +> performance than a JMS bridge. Also the core bridge can provide *once and +> only once* delivery guarantees without using XA. + +The bridge has built-in resilience to failure so if the source or target server +connection is lost, e.g. due to network failure, the bridge will retry +connecting to the source and/or target until they come back online. When it +comes back online it will resume operation as normal. + +The bridge can be configured with an optional JMS selector, so it will only +consume messages matching that JMS selector + +It can be configured to consume from a queue or a topic. When it consumes from +a topic it can be configured to consume using a non durable or durable +subscription + +The JMS Bridge is a simple POJO so can be deployed with most frameworks, simply +instantiate the `org.apache.activemq.artemis.api.jms.bridge.impl.JMSBridgeImpl` class and set the appropriate parameters. ## JMS Bridge Parameters -The main bean deployed is the `JMSBridge` bean. The bean is configurable -by the parameters passed to its constructor. +The main POJO is the `JMSBridge`. It is is configurable by the parameters +passed to its constructor. -> **Note** -> -> To let a parameter be unspecified (for example, if the authentication -> is anonymous or no message selector is provided), use ` />` for the unspecified parameter value. - -- Source Connection Factory Factory +- Source Connection Factory Factory - This injects the `SourceCFF` bean (also defined in the beans file). - This bean is used to create the *source* `ConnectionFactory` + This injects the `SourceCFF` bean (also defined in the beans file). This + bean is used to create the *source* `ConnectionFactory` -- Target Connection Factory Factory +- Target Connection Factory Factory - This injects the `TargetCFF` bean (also defined in the beans file). - This bean is used to create the *target* `ConnectionFactory` + This injects the `TargetCFF` bean (also defined in the beans file). This + bean is used to create the *target* `ConnectionFactory` -- Source Destination Factory Factory +- Source Destination Factory Factory - This injects the `SourceDestinationFactory` bean (also defined in - the beans file). This bean is used to create the *source* - `Destination` + This injects the `SourceDestinationFactory` bean (also defined in the beans + file). This bean is used to create the *source* `Destination` -- Target Destination Factory Factory +- Target Destination Factory Factory - This injects the `TargetDestinationFactory` bean (also defined in - the beans file). This bean is used to create the *target* - `Destination` + This injects the `TargetDestinationFactory` bean (also defined in the beans + file). This bean is used to create the *target* `Destination` -- Source User Name +- Source User Name - this parameter is the username for creating the *source* connection + this parameter is the username for creating the *source* connection -- Source Password +- Source Password - this parameter is the parameter for creating the *source* connection + this parameter is the parameter for creating the *source* connection -- Target User Name +- Target User Name - this parameter is the username for creating the *target* connection + this parameter is the username for creating the *target* connection -- Target Password +- Target Password - this parameter is the password for creating the *target* connection + this parameter is the password for creating the *target* connection -- Selector +- Selector - This represents a JMS selector expression used for consuming - messages from the source destination. Only messages that match the - selector expression will be bridged from the source to the target - destination + This represents a JMS selector expression used for consuming + messages from the source destination. Only messages that match the + selector expression will be bridged from the source to the target + destination - The selector expression must follow the [JMS selector - syntax](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html) + The selector expression must follow the [JMS selector + syntax](https://docs.oracle.com/javaee/7/api/javax/jms/Message.html) -- Failure Retry Interval +- Failure Retry Interval - This represents the amount of time in ms to wait between trying to - recreate connections to the source or target servers when the bridge - has detected they have failed + This represents the amount of time in ms to wait between trying to recreate + connections to the source or target servers when the bridge has detected they + have failed -- Max Retries +- Max Retries - This represents the number of times to attempt to recreate - connections to the source or target servers when the bridge has - detected they have failed. The bridge will give up after trying this - number of times. `-1` represents 'try forever' + This represents the number of times to attempt to recreate connections to the + source or target servers when the bridge has detected they have failed. The + bridge will give up after trying this number of times. `-1` represents 'try + forever' -- Quality Of Service +- Quality Of Service - This parameter represents the desired quality of service mode + This parameter represents the desired quality of service mode - Possible values are: + Possible values are: - - `AT_MOST_ONCE` + - `AT_MOST_ONCE` - - `DUPLICATES_OK` + - `DUPLICATES_OK` - - `ONCE_AND_ONLY_ONCE` + - `ONCE_AND_ONLY_ONCE` - See Quality Of Service section for a explanation of these modes. + See Quality Of Service section for a explanation of these modes. -- Max Batch Size +- Max Batch Size - This represents the maximum number of messages to consume from the - source destination before sending them in a batch to the target - destination. Its value must `>= 1` + This represents the maximum number of messages to consume from the source + destination before sending them in a batch to the target destination. Its value + must `>= 1` -- Max Batch Time +- Max Batch Time - This represents the maximum number of milliseconds to wait before - sending a batch to target, even if the number of messages consumed - has not reached `MaxBatchSize`. Its value must be `-1` to represent - 'wait forever', or `>= 1` to specify an actual time + This represents the maximum number of milliseconds to wait before sending a + batch to target, even if the number of messages consumed has not reached + `MaxBatchSize`. Its value must be `-1` to represent 'wait forever', or `>= 1` + to specify an actual time -- Subscription Name +- Subscription Name - If the source destination represents a topic, and you want to - consume from the topic using a durable subscription then this - parameter represents the durable subscription name + If the source destination represents a topic, and you want to consume from + the topic using a durable subscription then this parameter represents the + durable subscription name -- Client ID +- Client ID - If the source destination represents a topic, and you want to - consume from the topic using a durable subscription then this - attribute represents the the JMS client ID to use when - creating/looking up the durable subscription + If the source destination represents a topic, and you want to consume from + the topic using a durable subscription then this attribute represents the the + JMS client ID to use when creating/looking up the durable subscription -- Add MessageID In Header +- Add MessageID In Header - If `true`, then the original message's message ID will be appended - in the message sent to the destination in the header - `ACTIVEMQ_BRIDGE_MSG_ID_LIST`. If the message is bridged more than - once, each message ID will be appended. This enables a distributed - request-response pattern to be used + If `true`, then the original message's message ID will be appended in the + message sent to the destination in the header `ACTIVEMQ_BRIDGE_MSG_ID_LIST`. If + the message is bridged more than once, each message ID will be appended. This + enables a distributed request-response pattern to be used - > **Note** - > - > when you receive the message you can send back a response using - > the correlation id of the first message id, so when the original - > sender gets it back it will be able to correlate it. + > **Note:** + > + > when you receive the message you can send back a response using the + > correlation id of the first message id, so when the original sender gets it + > back it will be able to correlate it. -- MBean Server +- MBean Server - To manage the JMS Bridge using JMX, set the MBeanServer where the - JMS Bridge MBean must be registered (e.g. the JVM Platform - MBeanServer or JBoss AS MBeanServer) + To manage the JMS Bridge using JMX, set the MBeanServer where the JMS Bridge + MBean must be registered (e.g. the JVM Platform MBeanServer) -- ObjectName +- ObjectName - If you set the MBeanServer, you also need to set the ObjectName used - to register the JMS Bridge MBean (must be unique) + If you set the MBeanServer, you also need to set the ObjectName used to + register the JMS Bridge MBean (must be unique) The "transactionManager" property points to a JTA transaction manager implementation and should be set if you need to use the 'ONCE_AND_ONCE_ONLY' -Quality of Service. Apache ActiveMQ Artemis doesn't ship with such an implementation, but -if you are running within an Application Server you can inject the Transaction -Manager that is shipped. +Quality of Service. Apache ActiveMQ Artemis doesn't ship with such an +implementation, but if you are running within an Application Server you can +inject the Transaction Manager that is shipped. ## Source and Target Connection Factories -The source and target connection factory factories are used to create -the connection factory used to create the connection for the source or -target server. +The source and target connection factory factories are used to create the +connection factory used to create the connection for the source or target +server. -The configuration example above uses the default implementation provided -by Apache ActiveMQ Artemis that looks up the connection factory using JNDI. For other -Application Servers or JMS providers a new implementation may have to be +The configuration example above uses the default implementation provided by +Apache ActiveMQ Artemis that looks up the connection factory using JNDI. For +other Application Servers or JMS providers a new implementation may have to be provided. This can easily be done by implementing the interface `org.apache.activemq.artemis.jms.bridge.ConnectionFactoryFactory`. ## Source and Target Destination Factories -Again, similarly, these are used to create or lookup up the -destinations. +Again, similarly, these are used to create or lookup up the destinations. -In the configuration example above, we have used the default provided by -Apache ActiveMQ Artemis that looks up the destination using JNDI. +In the configuration example above, we have used the default provided by Apache +ActiveMQ Artemis that looks up the destination using JNDI. A new implementation can be provided by implementing `org.apache.activemq.artemis.jms.bridge.DestinationFactory` interface. ## Quality Of Service -The quality of service modes used by the bridge are described here in -more detail. +The quality of service modes used by the bridge are described here in more +detail. ### AT_MOST_ONCE -With this QoS mode messages will reach the destination from the source -at most once. The messages are consumed from the source and acknowledged -before sending to the destination. Therefore there is a possibility that -if failure occurs between removing them from the source and them -arriving at the destination they could be lost. Hence delivery will -occur at most once. +With this QoS mode messages will reach the destination from the source at most +once. The messages are consumed from the source and acknowledged before sending +to the destination. Therefore there is a possibility that if failure occurs +between removing them from the source and them arriving at the destination they +could be lost. Hence delivery will occur at most once. This mode is available for both durable and non-durable messages. @@ -240,71 +225,51 @@ This mode is available for both durable and non-durable messages. With this QoS mode, the messages are consumed from the source and then acknowledged after they have been successfully sent to the destination. -Therefore there is a possibility that if failure occurs after sending to -the destination but before acknowledging them, they could be sent again -when the system recovers. I.e. the destination might receive duplicates -after a failure. +Therefore there is a possibility that if failure occurs after sending to the +destination but before acknowledging them, they could be sent again when the +system recovers. I.e. the destination might receive duplicates after a failure. This mode is available for both durable and non-durable messages. ### ONCE_AND_ONLY_ONCE -This QoS mode ensures messages will reach the destination from the -source once and only once. (Sometimes this mode is known as "exactly -once"). If both the source and the destination are on the same Apache ActiveMQ Artemis -server instance then this can be achieved by sending and acknowledging -the messages in the same local transaction. If the source and -destination are on different servers this is achieved by enlisting the -sending and consuming sessions in a JTA transaction. The JTA transaction -is controlled by a JTA Transaction Manager which will need to be set -via the settransactionManager method on the Bridge. +This QoS mode ensures messages will reach the destination from the source once +and only once. (Sometimes this mode is known as "exactly once"). If both the +source and the destination are on the same Apache ActiveMQ Artemis server +instance then this can be achieved by sending and acknowledging the messages in +the same local transaction. If the source and destination are on different +servers this is achieved by enlisting the sending and consuming sessions in a +JTA transaction. The JTA transaction is controlled by a JTA Transaction Manager +which will need to be set via the settransactionManager method on the Bridge. This mode is only available for durable messages. -> **Note** +> **Note:** > -> For a specific application it may possible to provide once and only -> once semantics without using the ONCE\_AND\_ONLY\_ONCE QoS level. This -> can be done by using the DUPLICATES\_OK mode and then checking for -> duplicates at the destination and discarding them. Some JMS servers -> provide automatic duplicate message detection functionality, or this -> may be possible to implement on the application level by maintaining a -> cache of received message ids on disk and comparing received messages -> to them. The cache would only be valid for a certain period of time so -> this approach is not as watertight as using ONCE\_AND\_ONLY\_ONCE but -> may be a good choice depending on your specific application. +> For a specific application it may possible to provide once and only once +> semantics without using the ONCE\_AND\_ONLY\_ONCE QoS level. This can be done +> by using the DUPLICATES\_OK mode and then checking for duplicates at the +> destination and discarding them. Some JMS servers provide automatic duplicate +> message detection functionality, or this may be possible to implement on the +> application level by maintaining a cache of received message ids on disk and +> comparing received messages to them. The cache would only be valid for a +> certain period of time so this approach is not as watertight as using +> ONCE\_AND\_ONLY\_ONCE but may be a good choice depending on your specific +> application. ### Time outs and the JMS bridge -There is a possibility that the target or source server will not be -available at some point in time. If this occurs then the bridge will try -`Max Retries` to reconnect every `Failure Retry Interval` milliseconds -as specified in the JMS Bridge definition. - -However since a third party JNDI is used, in this case the JBoss naming -server, it is possible for the JNDI lookup to hang if the network were -to disappear during the JNDI lookup. To stop this from occurring the -JNDI definition can be configured to time out if this occurs. To do this -set the `jnp.timeout` and the `jnp.sotimeout` on the Initial Context -definition. The first sets the connection timeout for the initial -connection and the second the read timeout for the socket. - -> **Note** -> -> Once the initial JNDI connection has succeeded all calls are made -> using RMI. If you want to control the timeouts for the RMI connections -> then this can be done via system properties. JBoss uses Sun's RMI and -> the properties can be found -> [here](https://docs.oracle.com/javase/8/docs/technotes/guides/rmi/sunrmiproperties.html). -> The default connection timeout is 10 seconds and the default read -> timeout is 18 seconds. +There is a possibility that the target or source server will not be available +at some point in time. If this occurs then the bridge will try `Max Retries` to +reconnect every `Failure Retry Interval` milliseconds as specified in the JMS +Bridge definition. -If you implement your own factories for looking up JMS resources then -you will have to bear in mind timeout issues. +If you implement your own factories for looking up JMS resources then you will +have to bear in mind timeout issues. ### Examples -Please see [the examples chapter](examples.md) which shows how to configure and use a JMS Bridge with -JBoss AS to send messages to the source destination and consume them -from the target destination and how to configure and use a JMS Bridge between -two standalone Apache ActiveMQ Artemis servers. +Please see [JMS Bridge Example](examples.md#jms-bridge) which shows how to +programmatically instantiate and configure a JMS Bridge to send messages to the +source destination and consume them from the target destination between two +standalone Apache ActiveMQ Artemis brokers. diff --git a/docs/user-manual/en/jms-core-mapping.md b/docs/user-manual/en/jms-core-mapping.md index d7fe3fc5bfd..21c3d0c3008 100644 --- a/docs/user-manual/en/jms-core-mapping.md +++ b/docs/user-manual/en/jms-core-mapping.md @@ -1,15 +1,15 @@ # Mapping JMS Concepts to the Core API -This chapter describes how JMS destinations are mapped to Apache ActiveMQ Artemis -addresses. +This chapter describes how JMS destinations are mapped to Apache ActiveMQ +Artemis addresses. -Apache ActiveMQ Artemis core is JMS-agnostic. It does not have any concept of a JMS -topic. A JMS topic is implemented in core as an address with name=(the topic name) -and with a MULTICAST routing type with zero or more queues bound to it. Each queue bound to that address -represents a topic subscription. +Apache ActiveMQ Artemis core is JMS-agnostic. It does not have any concept of a +JMS topic. A JMS topic is implemented in core as an address with name=(the +topic name) and with a MULTICAST routing type with zero or more queues bound to +it. Each queue bound to that address represents a topic subscription. -Likewise, a JMS queue is implemented as an address with name=(the JMS queue name) with an ANYCAST routing type assocatied -with it. +Likewise, a JMS queue is implemented as an address with name=(the JMS queue +name) with an ANYCAST routing type associated with it. -Note. That whilst it is possible to configure a JMS topic and queue with the same name, it is not a recommended -configuration for use with cross protocol. +**Note:** While it is possible to configure a JMS topic and queue with the same +name, it is not a recommended configuration for use with cross protocol. \ No newline at end of file diff --git a/docs/user-manual/en/large-messages.md b/docs/user-manual/en/large-messages.md index 0ecb8666b9d..26188af19a8 100644 --- a/docs/user-manual/en/large-messages.md +++ b/docs/user-manual/en/large-messages.md @@ -1,154 +1,128 @@ # Large Messages -Apache ActiveMQ Artemis supports sending and receiving of huge messages, even when the -client and server are running with limited memory. The only realistic -limit to the size of a message that can be sent or consumed is the -amount of disk space you have available. We have tested sending and -consuming messages up to 8 GiB in size with a client and server running -in just 50MiB of RAM! - -To send a large message, the user can set an `InputStream` on a message -body, and when that message is sent, Apache ActiveMQ Artemis will read the -`InputStream`. A `FileInputStream` could be used for example to send a -huge message from a huge file on disk. - -As the `InputStream` is read the data is sent to the server as a stream -of fragments. The server persists these fragments to disk as it receives -them and when the time comes to deliver them to a consumer they are read -back of the disk, also in fragments and sent down the wire. When the -consumer receives a large message it initially receives just the message -with an empty body, it can then set an `OutputStream` on the message to -stream the huge message body to a file on disk or elsewhere. At no time -is the entire message body stored fully in memory, either on the client -or the server. +Apache ActiveMQ Artemis supports sending and receiving of huge messages, even +when the client and server are running with limited memory. The only realistic +limit to the size of a message that can be sent or consumed is the amount of +disk space you have available. We have tested sending and consuming messages up +to 8 GiB in size with a client and server running in just 50MiB of RAM! + +To send a large message, the user can set an `InputStream` on a message body, +and when that message is sent, Apache ActiveMQ Artemis will read the +`InputStream`. A `FileInputStream` could be used for example to send a huge +message from a huge file on disk. + +As the `InputStream` is read the data is sent to the server as a stream of +fragments. The server persists these fragments to disk as it receives them and +when the time comes to deliver them to a consumer they are read back of the +disk, also in fragments and sent down the wire. When the consumer receives a +large message it initially receives just the message with an empty body, it can +then set an `OutputStream` on the message to stream the huge message body to a +file on disk or elsewhere. At no time is the entire message body stored fully +in memory, either on the client or the server. ## Configuring the server -Large messages are stored on a disk directory on the server side, as -configured on the main configuration file. +Large messages are stored on a disk directory on the server side, as configured +on the main configuration file. -The configuration property `large-messages-directory` specifies where -large messages are stored. For JDBC persistence the `large-message-table` -should be configured. +The configuration property `large-messages-directory` specifies where large +messages are stored. For JDBC persistence the `large-message-table` should be +configured. ```xml - ... - /data/large-messages - ... - + ... + /data/large-messages + ... + + ``` -By default the large message directory is `data/largemessages` and `large-message-table` is -configured as "LARGE_MESSAGE_TABLE". +By default the large message directory is `data/largemessages` and +`large-message-table` is configured as "LARGE_MESSAGE_TABLE". -For the best performance we recommend using file store with large messages directory stored -on a different physical volume to the message journal or paging directory. +For the best performance we recommend using file store with large messages +directory stored on a different physical volume to the message journal or +paging directory. ## Configuring the Client -Any message larger than a certain size is considered a large message. -Large messages will be split up and sent in fragments. This is -determined by the URL parameter `minLargeMessageSize` +Any message larger than a certain size is considered a large message. Large +messages will be split up and sent in fragments. This is determined by the URL +parameter `minLargeMessageSize` -> **Note** +> **Note:** > -> Apache ActiveMQ Artemis messages are encoded using 2 bytes per character so if the -> message data is filled with ASCII characters (which are 1 byte) the -> size of the resulting Apache ActiveMQ Artemis message would roughly double. This is -> important when calculating the size of a "large" message as it may -> appear to be less than the `minLargeMessageSize` before it is sent, -> but it then turns into a "large" message once it is encoded. +> Apache ActiveMQ Artemis messages are encoded using 2 bytes per character so +> if the message data is filled with ASCII characters (which are 1 byte) the +> size of the resulting Apache ActiveMQ Artemis message would roughly double. +> This is important when calculating the size of a "large" message as it may +> appear to be less than the `minLargeMessageSize` before it is sent, but it +> then turns into a "large" message once it is encoded. The default value is 100KiB. -[Configuring the transport directly from the client side](configuring-transports.md) -will provide more information on how to instantiate the core session factory -or JMS connection factory. +[Configuring the transport directly from the client +side](configuring-transports.md#configuring-the-transport-directly-from-the-client) +will provide more information on how to instantiate the core session factory or +JMS connection factory. ## Compressed Large Messages You can choose to send large messages in compressed form using `compressLargeMessages` URL parameter. -If you specify the boolean URL parameter `compressLargeMessages` as true, -The system will use the ZIP algorithm to compress the message body as -the message is transferred to the server's side. Notice that there's no -special treatment at the server's side, all the compressing and uncompressing -is done at the client. +If you specify the boolean URL parameter `compressLargeMessages` as true, The +system will use the ZIP algorithm to compress the message body as the message +is transferred to the server's side. Notice that there's no special treatment +at the server's side, all the compressing and uncompressing is done at the +client. -If the compressed size of a large message is below `minLargeMessageSize`, -it is sent to server as regular messages. This means that the message won't -be written into the server's large-message data directory, thus reducing the -disk I/O. +If the compressed size of a large message is below `minLargeMessageSize`, it is +sent to server as regular messages. This means that the message won't be +written into the server's large-message data directory, thus reducing the disk +I/O. ## Streaming large messages -Apache ActiveMQ Artemis supports setting the body of messages using input and output -streams (`java.lang.io`) +Apache ActiveMQ Artemis supports setting the body of messages using input and +output streams (`java.lang.io`) -These streams are then used directly for sending (input streams) and -receiving (output streams) messages. +These streams are then used directly for sending (input streams) and receiving +(output streams) messages. -When receiving messages there are 2 ways to deal with the output stream; -you may choose to block while the output stream is recovered using the -method `ClientMessage.saveOutputStream` or alternatively using the -method `ClientMessage.setOutputstream` which will asynchronously write -the message to the stream. If you choose the latter the consumer must be -kept alive until the message has been fully received. +When receiving messages there are 2 ways to deal with the output stream; you +may choose to block while the output stream is recovered using the method +`ClientMessage.saveOutputStream` or alternatively using the method +`ClientMessage.setOutputstream` which will asynchronously write the message to +the stream. If you choose the latter the consumer must be kept alive until the +message has been fully received. -You can use any kind of stream you like. The most common use case is to -send files stored in your disk, but you could also send things like JDBC -Blobs, `SocketInputStream`, things you recovered from `HTTPRequests` -etc. Anything as long as it implements `java.io.InputStream` for sending -messages or `java.io.OutputStream` for receiving them. +You can use any kind of stream you like. The most common use case is to send +files stored in your disk, but you could also send things like JDBC Blobs, +`SocketInputStream`, things you recovered from `HTTPRequests` etc. Anything as +long as it implements `java.io.InputStream` for sending messages or +`java.io.OutputStream` for receiving them. ### Streaming over Core API -The following table shows a list of methods available at `ClientMessage` -which are also available through JMS by the use of object properties. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionJMS Equivalent
setBodyInputStream(InputStream)Set the InputStream used to read a message body when sending it.JMS_AMQ_InputStream
setOutputStream(OutputStream)Set the OutputStream that will receive the body of a message. This method does not block.JMS_AMQ_OutputStream
saveOutputStream(OutputStream)Save the body of the message to the `OutputStream`. It will block until the entire content is transferred to the `OutputStream`.JMS_AMQ_SaveStream
+The following table shows a list of methods available at `ClientMessage` which +are also available through JMS by the use of object properties. + +Name | Description | JMS Equivalent +---|---|--- +setBodyInputStream(InputStream)|Set the InputStream used to read a message body when sending it.|JMS_AMQ_InputStream +setOutputStream(OutputStream)|Set the OutputStream that will receive the body of a message. This method does not block.|JMS_AMQ_OutputStream +saveOutputStream(OutputStream)|Save the body of the message to the `OutputStream`. It will block until the entire content is transferred to the `OutputStream`.|JMS_AMQ_SaveStream To set the output stream when receiving a core message: ``` java ClientMessage msg = consumer.receive(...); - // This will block here until the stream was transferred msg.saveOutputStream(someOutputStream); @@ -165,16 +139,15 @@ ClientMessage msg = session.createMessage(); msg.setInputStream(dataInputStream); ``` -Notice also that for messages with more than 2GiB the getBodySize() will -return invalid values since this is an integer (which is also exposed to -the JMS API). On those cases you can use the message property -_AMQ_LARGE_SIZE. +Notice also that for messages with more than 2GiB the getBodySize() will return +invalid values since this is an integer (which is also exposed to the JMS API). +On those cases you can use the message property _AMQ_LARGE_SIZE. ### Streaming over JMS -When using JMS, Apache ActiveMQ Artemis maps the streaming methods on the core API (see -ClientMessage API table above) by setting object properties . You can use the method -`Message.setObjectProperty` to set the input and output streams. +When using JMS, Apache ActiveMQ Artemis maps the streaming methods on the core +API (see ClientMessage API table above) by setting object properties . You can +use the method `Message.setObjectProperty` to set the input and output streams. The `InputStream` can be defined through the JMS Object Property JMS_AMQ_InputStream on messages being sent: @@ -215,16 +188,16 @@ using the property JMS_AMQ_OutputStream. messageReceived.setObjectProperty("JMS_AMQ_OutputStream", bufferedOutput); ``` -> **Note** +> **Note:** > > When using JMS, Streaming large messages are only supported on > `StreamMessage` and `BytesMessage`. ### Streaming Alternative -If you choose not to use the `InputStream` or `OutputStream` capability -of Apache ActiveMQ Artemis You could still access the data directly in an alternative -fashion. +If you choose not to use the `InputStream` or `OutputStream` capability of +Apache ActiveMQ Artemis You could still access the data directly in an +alternative fashion. On the Core API just get the bytes of the body as you normally would. @@ -241,6 +214,7 @@ for (int i = 0 ; i < msg.getBodySize(); i += bytes.length) If using JMS API, `BytesMessage` and `StreamMessage` also supports it transparently. + ``` java BytesMessage rm = (BytesMessage)cons.receive(10000); @@ -255,5 +229,5 @@ for (int i = 0; i < rm.getBodyLength(); i += 1024) ## Large message example -Please see the [examples](examples.md) chapter for an example which shows -how large message is configured and used with JMS. +Please see the [Large Message Example](examples.md#large-message) which shows +how large messages are configured and used with JMS. diff --git a/docs/user-manual/en/last-value-queues.md b/docs/user-manual/en/last-value-queues.md index f242ff5210f..ea7cfc9815b 100644 --- a/docs/user-manual/en/last-value-queues.md +++ b/docs/user-manual/en/last-value-queues.md @@ -14,26 +14,26 @@ Last-Value queues can be statically configured via the `last-value` boolean property: ```xml - - - ... -

- - - -
- -
+
+ + + +
``` -Specified on creating a Queue by using the CORE api specifying the parameter `lastValue` to `true`. +Specified on creating a queue by using the CORE api specifying the parameter +`lastValue` to `true`. -Or on auto-create when using the JMS Client by using address parameters when creating the destination used by the consumer. +Or on auto-create when using the JMS Client by using address parameters when +creating the destination used by the consumer. - Queue queue = session.createQueue("my.destination.name?last-value=true"); - Topic topic = session.createTopic("my.destination.name?last-value=true"); +```java +Queue queue = session.createQueue("my.destination.name?last-value=true"); +Topic topic = session.createTopic("my.destination.name?last-value=true"); +``` -Also the default for all queues under and address can be defaulted using the address-setting configuration: +Also the default for all queues under and address can be defaulted using the +`address-setting` configuration: ```xml @@ -45,7 +45,8 @@ By default, `default-last-value-queue` is false. Address wildcards can be used to configure Last-Value queues for a set of addresses (see [here](wildcard-syntax.md)). -Note that address-setting `last-value-queue` config is deprecated, please use `default-last-value-queue` instead. +Note that `address-setting` `last-value-queue` config is deprecated, please use +`default-last-value-queue` instead. ## Last-Value Property @@ -77,5 +78,5 @@ System.out.format("Received message: %s\n", messageReceived.getText()); ## Example -See the [examples](examples.md) chapter for an example which shows how last value queues are configured -and used with JMS. +See the [last-value queue example](examples.md#last-value-queue) which shows +how last value queues are configured and used with JMS. diff --git a/docs/user-manual/en/libaio.md b/docs/user-manual/en/libaio.md index ad4aba5c7ca..51023efca1c 100644 --- a/docs/user-manual/en/libaio.md +++ b/docs/user-manual/en/libaio.md @@ -13,8 +13,8 @@ please see [Persistence](persistence.md). These are the native libraries distributed by Apache ActiveMQ Artemis: -- libartemis-native-64.so - x86 64 bits -- We distributed a 32-bit version until early 2017. While it's not available on the distribution any longer it should still be possible to compile to a 32-bit environment if needed. +- libartemis-native-64.so - x86 64 bits +- We distributed a 32-bit version until early 2017. While it's not available on the distribution any longer it should still be possible to compile to a 32-bit environment if needed. When using libaio, Apache ActiveMQ Artemis will always try loading these files as long as they are on the [library path](using-server.md#library-path) @@ -28,12 +28,15 @@ You can install libaio using the following steps as the root user: Using yum, (e.g. on Fedora or Red Hat Enterprise Linux): - yum install libaio +``` +yum install libaio +``` Using aptitude, (e.g. on Ubuntu or Debian system): - apt-get install libaio - +``` +apt-get install libaio +``` ## Compiling the native libraries @@ -44,26 +47,26 @@ those platforms with the release. ## Compilation dependencies -> **Note** +> **Note:** > > The native layer is only available on Linux. If you are > in a platform other than Linux the native compilation will not work These are the required linux packages to be installed for the compilation to work: -- gcc - C Compiler +- gcc - C Compiler -- gcc-c++ or g++ - Extension to gcc with support for C++ +- gcc-c++ or g++ - Extension to gcc with support for C++ -- libtool - Tool for link editing native libraries +- libtool - Tool for link editing native libraries -- libaio - library to disk asynchronous IO kernel functions +- libaio - library to disk asynchronous IO kernel functions -- libaio-dev - Compilation support for libaio +- libaio-dev - Compilation support for libaio -- cmake +- cmake -- A full JDK installed with the environment variable JAVA\_HOME set to +- A full JDK installed with the environment variable JAVA\_HOME set to its location To perform this installation on RHEL or Fedora, you can simply type this at a command line: @@ -74,7 +77,7 @@ Or on Debian systems: sudo apt-get install libtool gcc-g++ gcc libaio libaio- cmake -> **Note** +> **Note:** > > You could find a slight variation of the package names depending on > the version and Linux distribution. (for example gcc-c++ on Fedora diff --git a/docs/user-manual/en/logging.md b/docs/user-manual/en/logging.md index 9cad81767ce..662218c2b0b 100644 --- a/docs/user-manual/en/logging.md +++ b/docs/user-manual/en/logging.md @@ -7,63 +7,34 @@ the console and to a file. There are 6 loggers available which are as follows: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LoggerLogger Description
org.jboss.loggingLogs any calls not handled by the Apache ActiveMQ Artemis loggers
org.apache.activemq.artemis.core.serverLogs the core server
org.apache.activemq.artemis.utilsLogs utility calls
org.apache.activemq.artemis.journalLogs Journal calls
org.apache.activemq.artemis.jmsLogs JMS calls
org.apache.activemq.artemis.integration.bootstrap Logs bootstrap calls
- - : Global Configuration Properties +Logger | Description +---|--- +org.jboss.logging|Logs any calls not handled by the Apache ActiveMQ Artemis loggers +org.apache.activemq.artemis.core.server|Logs the core server +org.apache.activemq.artemis.utils|Logs utility calls +org.apache.activemq.artemis.journal|Logs Journal calls +org.apache.activemq.artemis.jms|Logs JMS calls +org.apache.activemq.artemis.integration.bootstrap|Logs bootstrap calls + ## Logging in a client or with an Embedded server Firstly, if you want to enable logging on the client side you need to -include the JBoss logging jars in your library. If you are using maven -add the following dependencies. - - - org.jboss.logmanager - jboss-logmanager - 1.5.3.Final - - - org.apache.activemq - activemq-core-client - 1.0.0.Final - +include the JBoss logging jars in your library. If you are using Maven +the simplest way is to use the "all" client jar. + +```xml + + org.jboss.logmanager + jboss-logmanager + 2.0.3.Final + + + org.apache.activemq + activemq-core-client + 2.5.0 + +``` There are 2 properties you need to set when starting your java program, the first is to set the Log Manager to use the JBoss Log Manager, this @@ -74,41 +45,43 @@ The second is to set the location of the logging.properties file to use, this is done via the `-Dlogging.configuration` for instance `-Dlogging.configuration=file:///home/user/projects/myProject/logging.properties`. -> **Note** +> **Note:** > -> The value for this needs to be valid URL +> The `logging.configuration` system property needs to be valid URL The following is a typical `logging.properties for a client` - # Root logger option - loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra - - # Root logger level - logger.level=INFO - # Apache ActiveMQ Artemis logger levels - logger.org.apache.activemq.artemis.core.server.level=INFO - logger.org.apache.activemq.artemis.utils.level=INFO - logger.org.apache.activemq.artemis.jms.level=DEBUG - - # Root logger handlers - logger.handlers=FILE,CONSOLE - - # Console handler configuration - handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler - handler.CONSOLE.properties=autoFlush - handler.CONSOLE.level=FINE - handler.CONSOLE.autoFlush=true - handler.CONSOLE.formatter=PATTERN - - # File handler configuration - handler.FILE=org.jboss.logmanager.handlers.FileHandler - handler.FILE.level=FINE - handler.FILE.properties=autoFlush,fileName - handler.FILE.autoFlush=true - handler.FILE.fileName=activemq.log - handler.FILE.formatter=PATTERN - - # Formatter pattern configuration - formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter - formatter.PATTERN.properties=pattern - formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %s%E%n +``` +# Root logger option +loggers=org.jboss.logging,org.apache.activemq.artemis.core.server,org.apache.activemq.artemis.utils,org.apache.activemq.artemis.journal,org.apache.activemq.artemis.jms,org.apache.activemq.artemis.ra + +# Root logger level +logger.level=INFO +# Apache ActiveMQ Artemis logger levels +logger.org.apache.activemq.artemis.core.server.level=INFO +logger.org.apache.activemq.artemis.utils.level=INFO +logger.org.apache.activemq.artemis.jms.level=DEBUG + +# Root logger handlers +logger.handlers=FILE,CONSOLE + +# Console handler configuration +handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler +handler.CONSOLE.properties=autoFlush +handler.CONSOLE.level=FINE +handler.CONSOLE.autoFlush=true +handler.CONSOLE.formatter=PATTERN + +# File handler configuration +handler.FILE=org.jboss.logmanager.handlers.FileHandler +handler.FILE.level=FINE +handler.FILE.properties=autoFlush,fileName +handler.FILE.autoFlush=true +handler.FILE.fileName=activemq.log +handler.FILE.formatter=PATTERN + +# Formatter pattern configuration +formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter +formatter.PATTERN.properties=pattern +formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %s%E%n +``` \ No newline at end of file diff --git a/docs/user-manual/en/management-console.md b/docs/user-manual/en/management-console.md index f542a01def3..d0ec2d8eaf5 100644 --- a/docs/user-manual/en/management-console.md +++ b/docs/user-manual/en/management-console.md @@ -4,7 +4,6 @@ Apache ActiveMQ Artemis ships by default with a management console. It is powere Its purpose is to expose the [Management API](management.md "Management API") via a user friendly web ui. - ## Login To access the management console use a browser and go to the URL [http://localhost:8161/console](). @@ -30,29 +29,27 @@ Once logged in you should be presented with a screen similar to. On the top right is small menu area you will see some icons. -- `question mark` This will load the artemis documentation in the console main window -- `person` will provide a drop down menu with -- - `about` this will load an about screen, here you will be able to see and validate versions -- - `log out` self descriptive. +- `question mark` This will load the artemis documentation in the console main window +- `person` will provide a drop down menu with +- `about` this will load an about screen, here you will be able to see and validate versions +- `log out` self descriptive. #### Navigation Tabs Running below the Navigation Menu you will see several default feature tabs. -- `Artemis` This is the core tab for Apache ActiveMQ Artemis specific functionality. The rest of this document will focus on this. +- `Artemis` This is the core tab for Apache ActiveMQ Artemis specific functionality. The rest of this document will focus on this. -- `Connect` This allows you to connect to a remote broker from the same console. +- `Connect` This allows you to connect to a remote broker from the same console. -- `Dashboard` Here you can create and save graphs and tables of metrics available via JMX, a default jvm health dashboard is provided. +- `Dashboard` Here you can create and save graphs and tables of metrics available via JMX, a default jvm health dashboard is provided. -- `JMX` This exposes the raw Jolokia JMX so you can browse/access all the JMX endpoints exposed by the JVM. +- `JMX` This exposes the raw Jolokia JMX so you can browse/access all the JMX endpoints exposed by the JVM. -- `Threads` This allows you to monitor the thread usage and their state. +- `Threads` This allows you to monitor the thread usage and their state. You can install further hawtio plugins if you wish to have further functionality. - - ## Artemis Tab Click `Artemis` in the top navigation bar to see the Artemis specific plugin. (The Artemis tab won't appear if there is no broker in this JVM). The Artemis plugin works very much the same as the JMX plugin however with a focus on interacting with an Artemis broker. @@ -71,8 +68,6 @@ This expands to show the current configured available `addresses`. Under the address you can expand to find the `queues` for the address exposing attributes - - ### Key Operations #### Creating a new Address diff --git a/docs/user-manual/en/management.md b/docs/user-manual/en/management.md index 65b0b53c58e..ca523b7a20e 100644 --- a/docs/user-manual/en/management.md +++ b/docs/user-manual/en/management.md @@ -1,53 +1,56 @@ # Management -Apache ActiveMQ Artemis has an extensive *management API* that allows a user to modify a -server configuration, create new resources (e.g. addresses and queues), -inspect these resources (e.g. how many messages are currently held in a -queue) and interact with it (e.g. to remove messages from a queue). Apache ActiveMQ Artemis -also allows clients to subscribe to management notifications. +Apache ActiveMQ Artemis has an extensive *management API* that allows a user to +modify a server configuration, create new resources (e.g. addresses and +queues), inspect these resources (e.g. how many messages are currently held in +a queue) and interact with it (e.g. to remove messages from a queue). Apache +ActiveMQ Artemis also allows clients to subscribe to management notifications. There are four ways to access Apache ActiveMQ Artemis management API: -- Using JMX -- *JMX* is the standard way to manage Java applications +- Using JMX -- *JMX* is the standard way to manage Java applications -- Using Jolokia -- Jolokia exposes the JMX API of an application through a *REST interface* +- Using Jolokia -- Jolokia exposes the JMX API of an application through a + *REST interface* -- Using the Core Client -- management operations are sent to Apache ActiveMQ Artemis - server using *Core Client messages* +- Using the Core Client -- management operations are sent to Apache ActiveMQ + Artemis server using *Core Client messages* -- Using the Core JMS Client -- management operations are sent to Apache ActiveMQ Artemis - server using *Core JMS Client messages* +- Using any JMS Client -- management operations are sent to Apache ActiveMQ + Artemis server using *JMS Client messages* -Although there are four different ways to manage Apache ActiveMQ Artemis, each API supports -the same functionality. If it is possible to manage a resource using JMX -it is also possible to achieve the same result using Core messages. +Although there are four different ways to manage Apache ActiveMQ Artemis, each +API supports the same functionality. If it is possible to manage a resource +using JMX it is also possible to achieve the same result using Core messages. -Besides the programmatic management interfaces, a *Web Console* and a Command Line -*management utility* are also available to administrators of ActiveMQ Artemis. +Besides these four management interfaces, a [Web Console](management-console.md) +and a Command Line *management utility* are also available to administrators of +ActiveMQ Artemis. -The choice depends on your requirements, your application settings and -your environment to decide which way suits you best. +The choice depends on your requirements, your application settings, and your +environment to decide which way suits you best. -> **Note** +> **Note:** > -> In version 2 of Apache ActiveMQ Artemis the syntax used for MBean Object names has changed significantly due to changes -> in the addressing scheme. See the documentation for each individual resource for details on the new syntax. +> In version 2 of Apache ActiveMQ Artemis the syntax used for MBean Object +> names has changed significantly due to changes in the addressing scheme. See +> the documentation for each individual resource for details on the new syntax. ## The Management API -Regardless of the way you *invoke* management operations, the management -API is the same. +Regardless of the way you *invoke* management operations, the management API is +the same. -For each *managed resource*, there exists a Java interface describing -what operations can be invoked for this type of resource. +For each *managed resource*, there exists a Java interface describing what +operations can be invoked for this type of resource. -To learn about available *management operations*, see the Javadoc -for these interfaces. They are located in the +To learn about available *management operations*, see the Javadoc for these +interfaces. They are located in the `org.apache.activemq.artemis.api.core.management` package and they are named with the word `Control` at the end. -The way to invoke management operations depends on whether JMX, Core -messages, or Core JMS messages are used. +The way to invoke management operations depends on whether JMX, Core messages, +or JMS messages are used. ### Management API @@ -57,224 +60,219 @@ For full details of the API please consult the Javadoc. In summary: The `ActiveMQServerControl` interface is the entry point for broker management. -- Listing, creating, deploying and destroying queues +- Listing, creating, deploying and destroying queues - A list of deployed queues can be retrieved using the - `getQueueNames()` method. + A list of deployed queues can be retrieved using the `getQueueNames()` + method. - Queues can be created or destroyed using the management - operations `createQueue()` or `deployQueue()` or `destroyQueue()` + Queues can be created or destroyed using the management operations + `createQueue()` or `deployQueue()` or `destroyQueue()`. - `createQueue` will fail if the queue already exists while - `deployQueue` will do nothing. + `createQueue` will fail if the queue already exists while `deployQueue` will + do nothing. -- Listing and closing remote connections +- Listing and closing remote connections - Client's remote addresses can be retrieved using - `listRemoteAddresses()`. It is also possible to close the - connections associated with a remote address using the - `closeConnectionsForAddress()` method. + Client's remote addresses can be retrieved using `listRemoteAddresses()`. It + is also possible to close the connections associated with a remote address + using the `closeConnectionsForAddress()` method. - Alternatively, connection IDs can be listed using - `listConnectionIDs()` and all the sessions for a given connection ID - can be listed using `listSessions()`. + Alternatively, connection IDs can be listed using `listConnectionIDs()` and + all the sessions for a given connection ID can be listed using + `listSessions()`. -- Transaction heuristic operations +- Transaction heuristic operations - In case of a server crash, when the server restarts, it it possible - that some transaction requires manual intervention. The - `listPreparedTransactions()` method lists the transactions which are - in the prepared states (the transactions are represented as opaque - Base64 Strings.) To commit or rollback a given prepared transaction, - the `commitPreparedTransaction()` or `rollbackPreparedTransaction()` - method can be used to resolve heuristic transactions. Heuristically - completed transactions can be listed using the - `listHeuristicCommittedTransactions()` and - `listHeuristicRolledBackTransactions` methods. + In case of a server crash, when the server restarts, it it possible that some + transaction requires manual intervention. The `listPreparedTransactions()` + method lists the transactions which are in the prepared states (the + transactions are represented as opaque Base64 Strings.) To commit or rollback a + given prepared transaction, the `commitPreparedTransaction()` or + `rollbackPreparedTransaction()` method can be used to resolve heuristic + transactions. Heuristically completed transactions can be listed using the + `listHeuristicCommittedTransactions()` and + `listHeuristicRolledBackTransactions` methods. -- Enabling and resetting Message counters +- Enabling and resetting Message counters - Message counters can be enabled or disabled using the - `enableMessageCounters()` or `disableMessageCounters()` method. To - reset message counters, it is possible to invoke - `resetAllMessageCounters()` and `resetAllMessageCounterHistories()` - methods. + Message counters can be enabled or disabled using the + `enableMessageCounters()` or `disableMessageCounters()` method. To reset + message counters, it is possible to invoke `resetAllMessageCounters()` and + `resetAllMessageCounterHistories()` methods. -- Retrieving the server configuration and attributes +- Retrieving the server configuration and attributes - The `ActiveMQServerControl` exposes Apache ActiveMQ Artemis server configuration - through all its attributes (e.g. `getVersion()` method to retrieve - the server's version, etc.) + The `ActiveMQServerControl` exposes Apache ActiveMQ Artemis server + configuration through all its attributes (e.g. `getVersion()` method to + retrieve the server's version, etc.) -- Listing, creating and destroying Core bridges and diverts +- Listing, creating and destroying Core bridges and diverts - A list of deployed core bridges (resp. diverts) can be retrieved - using the `getBridgeNames()` (resp. `getDivertNames()`) method. + A list of deployed core bridges (resp. diverts) can be retrieved using the + `getBridgeNames()` (resp. `getDivertNames()`) method. - Core bridges (resp. diverts) can be created or destroyed using the - management operations `createBridge()` and `destroyBridge()` (resp. - `createDivert()` and `destroyDivert()`). + Core bridges (resp. diverts) can be created or destroyed using the management + operations `createBridge()` and `destroyBridge()` (resp. `createDivert()` and + `destroyDivert()`). -- It is possible to stop the server and force failover to occur with - any currently attached clients. +- It is possible to stop the server and force failover to occur with any + currently attached clients. - To do this use the `forceFailover()` operation. + To do this use the `forceFailover()` operation. - > **Note** - > - > Since this method actually stops the server you will probably - > receive some sort of error depending on which management service - > you use to call it. + > **Note:** + > + > Since this method actually stops the server you will probably receive some + > sort of error depending on which management service you use to call it. #### Address Management Individual addresses can be managed using the `AddressControl` interface. -- Modifying roles and permissions for an address +- Modifying roles and permissions for an address - You can add or remove roles associated to a queue using the - `addRole()` or `removeRole()` methods. You can list all the roles - associated to the queue with the `getRoles()` method + You can add or remove roles associated to a queue using the `addRole()` or + `removeRole()` methods. You can list all the roles associated to the queue with + the `getRoles()` method #### Queue Management -The bulk of the management API deals with queues. The -`QueueControl` interface defines the queue management operations. +The bulk of the management API deals with queues. The `QueueControl` interface +defines the queue management operations. -Most of the management operations on queues take either a single message -ID (e.g. to remove a single message) or a filter (e.g. to expire all -messages with a given property.) +Most of the management operations on queues take either a single message ID +(e.g. to remove a single message) or a filter (e.g. to expire all messages with +a given property.) -> **Note** +> **Note:** > -> Passing `null` or an empty string in the `filter` parameter means that -> the management operation will be performed on *all messages* in a queue. +> Passing `null` or an empty string in the `filter` parameter means that the +> management operation will be performed on *all messages* in a queue. -- Expiring, sending to a dead letter address and moving messages +- Expiring, sending to a dead letter address and moving messages - Messages can be expired from a queue by using the `expireMessages()` - method. If an expiry address is defined, messages will be sent to - it, otherwise they are discarded. The queue's expiry address can be - set with the `setExpiryAddress()` method. + Messages can be expired from a queue by using the `expireMessages()` method. + If an expiry address is defined, messages will be sent to it, otherwise they + are discarded. The queue's expiry address can be set with the + `setExpiryAddress()` method. - Messages can also be sent to a dead letter address with the - `sendMessagesToDeadLetterAddress()` method. It returns the number of - messages which are sent to the dead letter address. If a dead letter - address is not defined, message are removed from the queue and - discarded. The queue's dead letter address can be set with the - `setDeadLetterAddress()` method. + Messages can also be sent to a dead letter address with the + `sendMessagesToDeadLetterAddress()` method. It returns the number of messages + which are sent to the dead letter address. If a dead letter address is not + defined, message are removed from the queue and discarded. The queue's dead + letter address can be set with the `setDeadLetterAddress()` method. - Messages can also be moved from a queue to another queue by using - the `moveMessages()` method. + Messages can also be moved from a queue to another queue by using the + `moveMessages()` method. -- Listing and removing messages +- Listing and removing messages - Messages can be listed from a queue by using the `listMessages()` - method which returns an array of `Map`, one `Map` for each message. + Messages can be listed from a queue by using the `listMessages()` method + which returns an array of `Map`, one `Map` for each message. - Messages can also be removed from the queue by using the - `removeMessages()` method which returns a `boolean` for the single - message ID variant or the number of removed messages for the filter - variant. The `removeMessages()` method takes a `filter` argument to - remove only filtered messages. Setting the filter to an empty string - will in effect remove all messages. + Messages can also be removed from the queue by using the `removeMessages()` + method which returns a `boolean` for the single message ID variant or the + number of removed messages for the filter variant. The `removeMessages()` + method takes a `filter` argument to remove only filtered messages. Setting the + filter to an empty string will in effect remove all messages. -- Counting messages +- Counting messages - The number of messages in a queue is returned by the - `getMessageCount()` method. Alternatively, the `countMessages()` - will return the number of messages in the queue which *match a given - filter*. + The number of messages in a queue is returned by the `getMessageCount()` + method. Alternatively, the `countMessages()` will return the number of messages + in the queue which *match a given filter*. -- Changing message priority +- Changing message priority - The message priority can be changed by using the - `changeMessagesPriority()` method which returns a `boolean` for the - single message ID variant or the number of updated messages for the - filter variant. + The message priority can be changed by using the `changeMessagesPriority()` + method which returns a `boolean` for the single message ID variant or the + number of updated messages for the filter variant. -- Message counters +- Message counters - Message counters can be listed for a queue with the - `listMessageCounter()` and `listMessageCounterHistory()` methods - (see Message Counters section). The message counters can also be - reset for a single queue using the `resetMessageCounter()` method. + Message counters can be listed for a queue with the `listMessageCounter()` + and `listMessageCounterHistory()` methods (see Message Counters section). The + message counters can also be reset for a single queue using the + `resetMessageCounter()` method. -- Retrieving the queue attributes +- Retrieving the queue attributes - The `QueueControl` exposes queue settings through its - attributes (e.g. `getFilter()` to retrieve the queue's filter if it - was created with one, `isDurable()` to know whether the queue is - durable or not, etc.) + The `QueueControl` exposes queue settings through its attributes (e.g. + `getFilter()` to retrieve the queue's filter if it was created with one, + `isDurable()` to know whether the queue is durable or not, etc.) -- Pausing and resuming Queues +- Pausing and resuming Queues - The `QueueControl` can pause and resume the underlying queue. When a - queue is paused, it will receive messages but will not deliver them. - When it's resumed, it'll begin delivering the queued messages, if - any. + The `QueueControl` can pause and resume the underlying queue. When a queue is + paused, it will receive messages but will not deliver them. When it's resumed, + it'll begin delivering the queued messages, if any. #### Other Resources Management -Apache ActiveMQ Artemis allows to start and stop its remote resources (acceptors, -diverts, bridges, etc.) so that a server can be taken off line for a -given period of time without stopping it completely (e.g. if other -management operations must be performed such as resolving heuristic -transactions). These resources are: +Apache ActiveMQ Artemis allows to start and stop its remote resources +(acceptors, diverts, bridges, etc.) so that a server can be taken off line for +a given period of time without stopping it completely (e.g. if other management +operations must be performed such as resolving heuristic transactions). These +resources are: -- Acceptors +- Acceptors - They can be started or stopped using the `start()` or. `stop()` - method on the `AcceptorControl` interface. The acceptors parameters - can be retrieved using the `AcceptorControl` attributes (see [Understanding Acceptors](configuring-transports.md)) + They can be started or stopped using the `start()` or. `stop()` method on the + `AcceptorControl` interface. The acceptors parameters can be retrieved using + the `AcceptorControl` attributes (see [Understanding + Acceptors](configuring-transports.md)) -- Diverts +- Diverts - They can be started or stopped using the `start()` or `stop()` - method on the `DivertControl` interface. Diverts - parameters can be retrieved using the `DivertControl` attributes - (see [Diverting and Splitting Message Flows)](diverts.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `DivertControl` interface. Diverts parameters can be retrieved using the + `DivertControl` attributes (see [Diverting and Splitting Message + Flows)](diverts.md)) -- Bridges +- Bridges - They can be started or stopped using the `start()` (resp. `stop()`) - method on the `BridgeControl` interface. Bridges parameters can be retrieved - using the `BridgeControl` attributes (see [Core bridges](core-bridges.md)) + They can be started or stopped using the `start()` (resp. `stop()`) method on + the `BridgeControl` interface. Bridges parameters can be retrieved using the + `BridgeControl` attributes (see [Core bridges](core-bridges.md)) -- Broadcast groups +- Broadcast groups - They can be started or stopped using the `start()` or `stop()` - method on the `BroadcastGroupControl` interface. Broadcast groups - parameters can be retrieved using the `BroadcastGroupControl` - attributes (see [Clusters](clusters.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `BroadcastGroupControl` interface. Broadcast groups parameters can be retrieved + using the `BroadcastGroupControl` attributes (see [Clusters](clusters.md)) -- Cluster connections +- Cluster connections - They can be started or stopped using the `start()` or `stop()` - method on the `ClusterConnectionControl` interface. Cluster - connections parameters can be retrieved using the - `ClusterConnectionControl` attributes (see [Clusters](clusters.md)) + They can be started or stopped using the `start()` or `stop()` method on the + `ClusterConnectionControl` interface. Cluster connections parameters can be + retrieved using the `ClusterConnectionControl` attributes (see + [Clusters](clusters.md)) ## Using Management Via JMX Apache ActiveMQ Artemis can be managed using [JMX](http://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html). -The management API is exposed by Apache ActiveMQ Artemis using MBeans interfaces. -Apache ActiveMQ Artemis registers its resources with the domain `org.apache.activemq.artemis`. +The management API is exposed by Apache ActiveMQ Artemis using MBeans +interfaces. Apache ActiveMQ Artemis registers its resources with the domain +`org.apache.activemq.artemis`. -For example, the `ObjectName` to manage the anycast queue `exampleQueue` on the address `exampleAddress` is: +For example, the `ObjectName` to manage the anycast queue `exampleQueue` on the +address `exampleAddress` is: - org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` +org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` and the MBean is: - org.apache.activemq.artemis.api.core.management.QueueControl +``` +org.apache.activemq.artemis.api.core.management.QueueControl +``` The MBean `ObjectName`'s are built using the helper class `org.apache.activemq.artemis.api.core.management.ObjectNameBuilder`. You can -also use `jconsole` to find the `ObjectName` of the MBean you want to -manage. +also use `jconsole` to find the `ObjectName` of the MBean you want to manage. Example usage of the `ObjectNameBuilder` to obtain `ActiveMQServerControl`'s name: @@ -284,39 +282,43 @@ objectNameBuilder = ObjectNameBuilder.create(ArtemisResolver.DEFAULT_DOMAIN, bro serverObjectName = objectNameBuilder.getActiveMQServerObjectName() ``` -Managing Apache ActiveMQ Artemis using JMX is identical to management of any Java -Applications using JMX. It can be done by reflection or by creating +Managing Apache ActiveMQ Artemis using JMX is identical to management of any +Java Applications using JMX. It can be done by reflection or by creating proxies of the MBeans. ### Configuring JMX -By default, JMX is enabled to manage Apache ActiveMQ Artemis. It can be disabled by -setting `jmx-management-enabled` to `false` in -`broker.xml`: +By default, JMX is enabled to manage Apache ActiveMQ Artemis. It can be +disabled by setting `jmx-management-enabled` to `false` in `broker.xml`: - - false - +```xml + +false +``` + #### Role Based Authorisation for JMX -Although by default Artemis uses the Java Virtual Machine's `Platform MBeanServer` -this is guarded using role based authentication that leverages Artemis's JAAS plugin support. -This is configured via the `authorisation` element in the `management.xml` configuration file -and can be used to restrict access to attributes and methods on mbeans. +Although by default Artemis uses the Java Virtual Machine's `Platform +MBeanServer` this is guarded using role based authentication that leverages +Artemis's JAAS plugin support. This is configured via the `authorisation` +element in the `management.xml` configuration file and can be used to restrict +access to attributes and methods on mbeans. -There are 3 elements within the `authorisation` element, `whitelist`, `default-access` and -`role-access`, Lets discuss each in turn. +There are 3 elements within the `authorisation` element, `whitelist`, +`default-access` and `role-access`, Lets discuss each in turn. -Whitelist contains a list of mBeans that will by pass the authentication, this is typically -used for any mbeans that are needed by the console to run etc. The default configuration is: +Whitelist contains a list of mBeans that will by pass the authentication, this +is typically used for any mbeans that are needed by the console to run etc. The +default configuration is: ```xml ``` -This means that any mbean with the domain `hawtio` will be allowed access without authorisation. for instance -`hawtio:plugin=artemis`. You can also use wildcards for the mBean properties so the following would also match. +This means that any mbean with the domain `hawtio` will be allowed access +without authorisation. for instance `hawtio:plugin=artemis`. You can also use +wildcards for the mBean properties so the following would also match. ```xml @@ -324,32 +326,35 @@ This means that any mbean with the domain `hawtio` will be allowed access withou ``` -The `role-access`defines how roles are mapped to particular mBeans and its attributes and methods, -the default configuration looks like: +The `role-access`defines how roles are mapped to particular mBeans and its +attributes and methods, the default configuration looks like: ```xml - - - - - - - + + + + + + + ``` -This contains 1 match and will be applied to any mBean that has the domain `org.apache.activemq.artemis`. -Any access to any mBeans that have this domain are controlled by the `access` elements which contain a -method and a set of roles. The method being invoked will be used to pick the closest matching method and -the roles for this will be applied for access. For instance if you try the invoke a method called `listMessages` on an mBean -with the `org.apache.activemq.artemis` domain then this would match the `access` with the method of `list*`. -You could also explicitly configure this by using the full method name, like so: +This contains 1 match and will be applied to any mBean that has the domain +`org.apache.activemq.artemis`. Any access to any mBeans that have this domain +are controlled by the `access` elements which contain a method and a set of +roles. The method being invoked will be used to pick the closest matching +method and the roles for this will be applied for access. For instance if you +try the invoke a method called `listMessages` on an mBean with the +`org.apache.activemq.artemis` domain then this would match the `access` with +the method of `list*`. You could also explicitly configure this by using the +full method name, like so: ```xml ``` -You can also match specific mBeans within a domain by adding a key attribute that is used to match one of the properties -on the mBean, like: +You can also match specific mBeans within a domain by adding a key attribute +that is used to match one of the properties on the mBean, like: ```xml @@ -360,9 +365,11 @@ on the mBean, like: ``` -You could also match a specific queue for instance : +You could also match a specific queue for instance: -`org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue"` +``` +org.apache.activemq.artemis:broker=,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue" +``` by configuring: @@ -376,85 +383,111 @@ by configuring: ``` -Access to JMX mBean attributes are converted to method calls so these are controlled via the `set*`, `get*` and `is*`. -The `*` access is the catch all for everything other method that isn't specifically matched. +Access to JMX mBean attributes are converted to method calls so these are +controlled via the `set*`, `get*` and `is*`. The `*` access is the catch all +for everything other method that isn't specifically matched. -The `default-access` element is basically the catch all for every method call that isn't handled via the `role-access` configuration. -This has teh same semantics as a `match` element. +The `default-access` element is basically the catch all for every method call +that isn't handled via the `role-access` configuration. This has teh same +semantics as a `match` element. -> **Note** +> **Note:** > -> If JMX is enabled, Apache ActiveMQ Artemis can *not* be managed locally using `jconsole` when connecting as a local process, -> this is because jconsole does not using any authentication when connecting this way. If you want to use jconsole you will -either have to disable authentication, by removing the `authentication` element or enable remote access. +> If JMX is enabled, Apache ActiveMQ Artemis can *not* be managed locally using +> `jconsole` when connecting as a local process, this is because jconsole does +> not using any authentication when connecting this way. If you want to use +> jconsole you will either have to disable authentication, by removing the +> `authentication` element or enable remote access. #### Configuring remote JMX Access By default remote JMX access to Artemis is disabled for security reasons. -Artemis has a JMX agent which allows access to JMX mBeans remotely. This is configured via the `connector` element in the -`management.xml` configuration file. To enable this you simply add the following xml: +Artemis has a JMX agent which allows access to JMX mBeans remotely. This is +configured via the `connector` element in the `management.xml` configuration +file. To enable this you simply add the following xml: ```xml ``` -This exposes the agent remotely on the port 1099. If you were connecting via jconsole you would connect as a remote process -using the service url `service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi` and an appropriate user name and password. +This exposes the agent remotely on the port 1099. If you were connecting via +jconsole you would connect as a remote process using the service url +`service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi` and an appropriate user +name and password. You can also configure the connector using the following: -- connector-host +- `connector-host` - The host to expose the agent on -- connector-port + The host to expose the agent on. + +- `connector-port` - The port to expose the agent on -- jmx-realm + The port to expose the agent on. - The jmx realm to use for authentication, defaults to `activemq` to match the JAAS configuration. -- object-name +- `jmx-realm` - The object name to expose the remote connector on, default is `connector:name=rmi` -- secured + The jmx realm to use for authentication, defaults to `activemq` to match the + JAAS configuration. + +- `object-name` - Whether the connector is secured using SSL -- key-store-path + The object name to expose the remote connector on; default is + `connector:name=rmi`. + +- `secured` - The location of the keystore -- key-store-password + Whether the connector is secured using SSL. + +- `key-store-path` - The keystore password -- key-store-provider - The provider, JKS by default -- trust-store-path + The location of the keystore. - The location of the truststore -- trust-store-password +- `key-store-password` - The trustore password -- trust-store-provider + The keystore password. - The provider, JKS by default +- `key-store-provider` -> **Note** + The provider; `JKS` by default. + +- `trust-store-path` + + The location of the truststore. + +- `trust-store-password` + + The trustore password. + +- `trust-store-provider` + + The provider; `JKS` by default. + +> **Note:** > -> It is important to note that the rmi registry will pick an ip address to bind to, If you have a multi IP addresses/NICs -> present on the system then you can choose the ip address to use by adding the following to artemis.profile +> It is important to note that the rmi registry will pick an ip address to bind +> to, If you have a multi IP addresses/NICs present on the system then you can +> choose the ip address to use by adding the following to artemis.profile > `-Djava.rmi.server.hostname=localhost` -> **Note** +> **Note:** > -> Remote connections using the default JVM Agent not enabled by default as Artemis exposes the mBean Server via its own configuration. -> This is so Artemis can leverage the JAAS authentication layer via JMX. If you want to expose this then you will need to -> disable both the connector and the authorisation by removing them from the `management.xml` configuration. -> Please refer to [Java Management guide](https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html) -> to configure the server for remote management (system properties must be set in `artemis.profile`). - -By default, Apache ActiveMQ Artemis server uses the JMX domain "org.apache.activemq.artemis". -To manage several Apache ActiveMQ Artemis servers from the *same* MBeanServer, the JMX -domain can be configured for each individual Apache ActiveMQ Artemis server by setting -`jmx-domain` in `broker.xml`: +> Remote connections using the default JVM Agent not enabled by default as +> Artemis exposes the mBean Server via its own configuration. This is so +> Artemis can leverage the JAAS authentication layer via JMX. If you want to +> expose this then you will need to disable both the connector and the +> authorisation by removing them from the `management.xml` configuration. +> Please refer to [Java Management +> guide](https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html) +> to configure the server for remote management (system properties must be set +> in `artemis.profile`). + +By default, Apache ActiveMQ Artemis server uses the JMX domain +"org.apache.activemq.artemis". To manage several Apache ActiveMQ Artemis +servers from the *same* MBeanServer, the JMX domain can be configured for each +individual Apache ActiveMQ Artemis server by setting `jmx-domain` in +`broker.xml`: ```xml @@ -463,81 +496,87 @@ domain can be configured for each individual Apache ActiveMQ Artemis server by s ### Example -See the [Examples](examples.md) chapter for an example which shows how to use a remote connection to JMX -and MBean proxies to manage Apache ActiveMQ Artemis. +See the [JMX Management Example](examples.md#jmx-management) which shows how to +use a remote connection to JMX and MBean proxies to manage Apache ActiveMQ +Artemis. ### Exposing JMX using Jolokia The default Broker configuration ships with the [Jolokia](https://jolokia.org) -HTTP agent deployed as a Web Application. Jolokia is a remote -JMX-over-HTTP bridge that exposes MBeans. For a full guide as -to how to use it refer to [Jolokia Documentation](https://jolokia.org/documentation.html), -however a simple example to query the broker's version would -be to use a browser and go to the URL [http://username:password@localhost:8161/console/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version](). +HTTP agent deployed as a web application. Jolokia is a remote JMX-over-HTTP +bridge that exposes MBeans. For a full guide as to how to use it refer to +[Jolokia Documentation](https://jolokia.org/documentation.html), however a +simple example to query the broker's version would be to use a browser and go +to the URL +[http://username:password@localhost:8161/console/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version](). This would give you back something like the following: - {"request":{"mbean":"org.apache.activemq.artemis:broker=\"0.0.0.0\"","attribute":"Version","type":"read"},"value":"2.0.0-SNAPSHOT","timestamp":1487017918,"status":200} +``` +{"request":{"mbean":"org.apache.activemq.artemis:broker=\"0.0.0.0\"","attribute":"Version","type":"read"},"value":"2.0.0-SNAPSHOT","timestamp":1487017918,"status":200} +``` ### JMX and the Console -The console that ships with Artemis uses Jolokia under the covers which in turn uses JMX. This will use the authentication -configuration in the `management.xml` file as described in the previous section. This means that when mBeans are accessed -via the console the credentials used to log into the console and the roles associated with them. By default access to the -console is only allow via users with the amq role. This is configured in the `artemis.profile` via the system property `-Dhawtio.role=amq`. -You can configure multiple roles by changing this to `-Dhawtio.roles=amq,view,update`. +The console that ships with Artemis uses Jolokia under the covers which in turn +uses JMX. This will use the authentication configuration in the +`management.xml` file as described in the previous section. This means that +when mBeans are accessed via the console the credentials used to log into the +console and the roles associated with them. By default access to the console is +only allow via users with the amq role. This is configured in the +`artemis.profile` via the system property `-Dhawtio.role=amq`. You can +configure multiple roles by changing this to `-Dhawtio.roles=amq,view,update`. -If a user doesn't have the correct role to invoke a specific operation then this will display an authorisation exception -in the console. +If a user doesn't have the correct role to invoke a specific operation then +this will display an authorisation exception in the console. ## Using Management Message API -The management message API in ActiveMQ Artemis is accessed by sending Core Client messages -to a special address, the *management address*. +The management message API in ActiveMQ Artemis is accessed by sending Core +Client messages to a special address, the *management address*. *Management messages* are regular Core Client messages with well-known -properties that the server needs to understand to interact with the -management API: +properties that the server needs to understand to interact with the management +API: -- The name of the managed resource +- The name of the managed resource -- The name of the management operation +- The name of the management operation -- The parameters of the management operation +- The parameters of the management operation -When such a management message is sent to the management address, -Apache ActiveMQ Artemis server will handle it, extract the information, invoke the +When such a management message is sent to the management address, Apache +ActiveMQ Artemis server will handle it, extract the information, invoke the operation on the managed resources and send a *management reply* to the management message's reply-to address (specified by `ClientMessageImpl.REPLYTO_HEADER_NAME`). -A `ClientConsumer` can be used to consume the management reply and -retrieve the result of the operation (if any) stored in the reply's -body. For portability, results are returned as a [JSON](https://json.org) -String rather than Java Serialization (the +A `ClientConsumer` can be used to consume the management reply and retrieve the +result of the operation (if any) stored in the reply's body. For portability, +results are returned as a [JSON](https://json.org) String rather than Java +Serialization (the `org.apache.activemq.artemis.api.core.management.ManagementHelper` can be used to convert the JSON string to Java objects). -These steps can be simplified to make it easier to invoke management -operations using Core messages: +These steps can be simplified to make it easier to invoke management operations +using Core messages: -1. Create a `ClientRequestor` to send messages to the management - address and receive replies +1. Create a `ClientRequestor` to send messages to the management address and + receive replies -2. Create a `ClientMessage` +2. Create a `ClientMessage` -3. Use the helper class - `org.apache.activemq.artemis.api.core.management.ManagementHelper` to fill - the message with the management properties +3. Use the helper class + `org.apache.activemq.artemis.api.core.management.ManagementHelper` to fill + the message with the management properties -4. Send the message using the `ClientRequestor` +4. Send the message using the `ClientRequestor` -5. Use the helper class - `org.apache.activemq.artemis.api.core.management.ManagementHelper` to - retrieve the operation result from the management reply +5. Use the helper class + `org.apache.activemq.artemis.api.core.management.ManagementHelper` to + retrieve the operation result from the management reply. -For example, to find out the number of messages in the queue -`exampleQueue`: +For example, to find out the number of messages in the queue `exampleQueue`: ```java ClientSession session = ... @@ -550,35 +589,38 @@ int count = (Integer) ManagementHelper.getResult(reply); System.out.println("There are " + count + " messages in exampleQueue"); ``` -Management operation name and parameters must conform to the Java -interfaces defined in the `management` packages. +Management operation name and parameters must conform to the Java interfaces +defined in the `management` packages. Names of the resources are built using the helper class `org.apache.activemq.artemis.api.core.management.ResourceNames` and are straightforward (e.g. `queue.exampleQueue` for `QueueControl` of the Queue `exampleQueue`, or `broker` for the `ActiveMQServerControl`). -> *NOTE* +> **Note:** > -> The `ManagementHelper` class can be used only with Core JMS messages. -> When called with a message from a different JMS library, an exception will be thrown. +> The `ManagementHelper` class can be used only with Core JMS messages. When +> called with a message from a different JMS library, an exception will be +> thrown. ### Configuring Management The management address to send management messages is configured in `broker.xml`: - activemq.management +```xml +activemq.management +``` By default, the address is `activemq.management`. -The management address requires a *special* user permission `manage` to -be able to receive and handle management messages. This is also -configured in broker.xml: +The management address requires a *special* user permission `manage` to be able +to receive and handle management messages. This is also configured in +broker.xml: ```xml - + @@ -586,49 +628,50 @@ configured in broker.xml: ### Example -See the [Examples](examples.md) chapter for an example which shows -how to use JMS messages to manage the Apache ActiveMQ Artemis server. +See the [Management Example](examples.md#management) which shows how to use JMS +messages to manage the Apache ActiveMQ Artemis server. ## Management Notifications -Apache ActiveMQ Artemis emits *notifications* to inform listeners of potentially -interesting events (creation of new resources, security violation, +Apache ActiveMQ Artemis emits *notifications* to inform listeners of +potentially interesting events (creation of new resources, security violation, etc.). These notifications can be received by two different ways: -- JMX notifications +- JMX notifications -- Notification messages +- Notification messages ### JMX Notifications -If JMX is enabled (see Configuring JMX section), JMX notifications can be received by -subscribing to `org.apache.activemq.artemis:type=Broker,brokerName=,module=Core,serviceType=Server` for -notifications on resources. +If JMX is enabled (see Configuring JMX section), JMX notifications can be +received by subscribing to +`org.apache.activemq.artemis:type=Broker,brokerName=,module=Core,serviceType=Server` for notifications on resources. ### Notification Messages -Apache ActiveMQ Artemis defines a special *management notification address*. -Queues can be bound to this address so that clients will receive -management notifications as messages. +Apache ActiveMQ Artemis defines a special *management notification address*. +Queues can be bound to this address so that clients will receive management +notifications as messages. -A client which wants to receive management notifications must -create a queue bound to the management notification address. It can -then receive the notifications from its queue. +A client which wants to receive management notifications must create a queue +bound to the management notification address. It can then receive the +notifications from its queue. -Notifications messages are regular messages with additional -properties corresponding to the notification (its type, when it -occurred, the resources which were concerned, etc.). +Notifications messages are regular messages with additional properties +corresponding to the notification (its type, when it occurred, the resources +which were concerned, etc.). -Since notifications are regular messages, it is possible to use -message selectors to filter out notifications and receives only a subset -of all the notifications emitted by the server. +Since notifications are regular messages, it is possible to use message +selectors to filter out notifications and receives only a subset of all the +notifications emitted by the server. #### Configuring The Management Notification Address -The management notification address to receive management notifications -is configured in `broker.xml`: +The management notification address to receive management notifications is +configured in `broker.xml`: ```xml activemq.notifications @@ -645,195 +688,186 @@ Topic notificationsTopic = ActiveMQJMSClient.createTopic("activemq.notifications Session session = ... MessageConsumer notificationConsumer = session.createConsumer(notificationsTopic); -notificationConsumer.setMessageListener(new MessageListener() -{ - public void onMessage(Message notif) - { - System.out.println("------------------------"); - System.out.println("Received notification:"); - try - { - Enumeration propertyNames = notif.getPropertyNames(); - while (propertyNames.hasMoreElements()) - { - String propertyName = (String)propertyNames.nextElement(); - System.out.format(" %s: %s\n", propertyName, notif.getObjectProperty(propertyName)); - } - } - catch (JMSException e) - { - } - System.out.println("------------------------"); +notificationConsumer.setMessageListener(new MessageListener() { + public void onMessage(Message notif) { + System.out.println("------------------------"); + System.out.println("Received notification:"); + try { + Enumeration propertyNames = notif.getPropertyNames(); + while (propertyNames.hasMoreElements()) { + String propertyName = (String)propertyNames.nextElement(); + System.out.format(" %s: %s\n", propertyName, notif.getObjectProperty(propertyName)); + } + } catch (JMSException e) { + } + System.out.println("------------------------"); } }); ``` ### Example -See the [Examples](examples.md) chapter for an example which shows how to use a JMS `MessageListener` to receive management notifications from ActiveMQ Artemis server. +See the [Management Notification Example](examples.md#management-notification) +which shows how to use a JMS `MessageListener` to receive management +notifications from ActiveMQ Artemis server. ### Notification Types and Headers -Below is a list of all the different kinds of notifications as well as -which headers are on the messages. Every notification has a -`_AMQ_NotifType` (value noted in parentheses) and `_AMQ_NotifTimestamp` -header. The timestamp is the un-formatted result of a call to -`java.lang.System.currentTimeMillis()`. +Below is a list of all the different kinds of notifications as well as which +headers are on the messages. Every notification has a `_AMQ_NotifType` (value +noted in parentheses) and `_AMQ_NotifTimestamp` header. The timestamp is the +un-formatted result of a call to `java.lang.System.currentTimeMillis()`. -- `BINDING_ADDED` (0) +- `BINDING_ADDED` (0) - `_AMQ_Binding_Type`, `_AMQ_Address`, `_AMQ_ClusterName`, - `_AMQ_RoutingName`, `_AMQ_Binding_ID`, `_AMQ_Distance`, - `_AMQ_FilterString` + `_AMQ_Binding_Type`, `_AMQ_Address`, `_AMQ_ClusterName`, + `_AMQ_RoutingName`, `_AMQ_Binding_ID`, `_AMQ_Distance`, + `_AMQ_FilterString` -- `BINDING_REMOVED` (1) +- `BINDING_REMOVED` (1) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, - `_AMQ_Binding_ID`, `_AMQ_Distance`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, + `_AMQ_Binding_ID`, `_AMQ_Distance`, `_AMQ_FilterString` -- `CONSUMER_CREATED` (2) +- `CONSUMER_CREATED` (2) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, - `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, - `_AMQ_SessionName`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, + `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, + `_AMQ_SessionName`, `_AMQ_FilterString` -- `CONSUMER_CLOSED` (3) +- `CONSUMER_CLOSED` (3) - `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, - `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, - `_AMQ_SessionName`, `_AMQ_FilterString` + `_AMQ_Address`, `_AMQ_ClusterName`, `_AMQ_RoutingName`, `_AMQ_Distance`, + `_AMQ_ConsumerCount`, `_AMQ_User`, `_AMQ_RemoteAddress`, + `_AMQ_SessionName`, `_AMQ_FilterString` -- `SECURITY_AUTHENTICATION_VIOLATION` (6) +- `SECURITY_AUTHENTICATION_VIOLATION` (6) - `_AMQ_User` + `_AMQ_User` -- `SECURITY_PERMISSION_VIOLATION` (7) +- `SECURITY_PERMISSION_VIOLATION` (7) - `_AMQ_Address`, `_AMQ_CheckType`, `_AMQ_User` + `_AMQ_Address`, `_AMQ_CheckType`, `_AMQ_User` -- `DISCOVERY_GROUP_STARTED` (8) +- `DISCOVERY_GROUP_STARTED` (8) - `name` + `name` -- `DISCOVERY_GROUP_STOPPED` (9) +- `DISCOVERY_GROUP_STOPPED` (9) - `name` + `name` -- `BROADCAST_GROUP_STARTED` (10) +- `BROADCAST_GROUP_STARTED` (10) - `name` + `name` -- `BROADCAST_GROUP_STOPPED` (11) +- `BROADCAST_GROUP_STOPPED` (11) - `name` + `name` -- `BRIDGE_STARTED` (12) +- `BRIDGE_STARTED` (12) - `name` + `name` -- `BRIDGE_STOPPED` (13) +- `BRIDGE_STOPPED` (13) - `name` + `name` -- `CLUSTER_CONNECTION_STARTED` (14) +- `CLUSTER_CONNECTION_STARTED` (14) - `name` + `name` -- `CLUSTER_CONNECTION_STOPPED` (15) +- `CLUSTER_CONNECTION_STOPPED` (15) - `name` + `name` -- `ACCEPTOR_STARTED` (16) +- `ACCEPTOR_STARTED` (16) - `factory`, `id` + `factory`, `id` -- `ACCEPTOR_STOPPED` (17) +- `ACCEPTOR_STOPPED` (17) - `factory`, `id` + `factory`, `id` -- `PROPOSAL` (18) +- `PROPOSAL` (18) - `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, `_AMQ_Binding_Type`, - `_AMQ_Address`, `_AMQ_Distance` + `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, `_AMQ_Binding_Type`, + `_AMQ_Address`, `_AMQ_Distance` -- `PROPOSAL_RESPONSE` (19) +- `PROPOSAL_RESPONSE` (19) - `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, - `_JBM_ProposalAltValue`, `_AMQ_Binding_Type`, `_AMQ_Address`, - `_AMQ_Distance` + `_JBM_ProposalGroupId`, `_JBM_ProposalValue`, + `_JBM_ProposalAltValue`, `_AMQ_Binding_Type`, `_AMQ_Address`, + `_AMQ_Distance` -- `CONSUMER_SLOW` (21) +- `CONSUMER_SLOW` (21) - `_AMQ_Address`, `_AMQ_ConsumerCount`, `_AMQ_RemoteAddress`, - `_AMQ_ConnectionName`, `_AMQ_ConsumerName`, `_AMQ_SessionName` + `_AMQ_Address`, `_AMQ_ConsumerCount`, `_AMQ_RemoteAddress`, + `_AMQ_ConnectionName`, `_AMQ_ConsumerName`, `_AMQ_SessionName` ## Message Counters -Message counters can be used to obtain information on queues *over time* -as Apache ActiveMQ Artemis keeps a history on queue metrics. +Message counters can be used to obtain information on queues *over time* as +Apache ActiveMQ Artemis keeps a history on queue metrics. -They can be used to show *trends* on queues. For example, using the -management API, it would be possible to query the number of messages in -a queue at regular interval. However, this would not be enough to know -if the queue is used: the number of messages can remain constant because -nobody is sending or receiving messages from the queue or because there -are as many messages sent to the queue than messages consumed from it. -The number of messages in the queue remains the same in both cases but -its use is widely different. +They can be used to show *trends* on queues. For example, using the management +API, it would be possible to query the number of messages in a queue at regular +interval. However, this would not be enough to know if the queue is used: the +number of messages can remain constant because nobody is sending or receiving +messages from the queue or because there are as many messages sent to the queue +than messages consumed from it. The number of messages in the queue remains +the same in both cases but its use is widely different. Message counters give additional information about the queues: -- `count` +- `count` - The *total* number of messages added to the queue since the server - was started + The *total* number of messages added to the queue since the server was + started -- `countDelta` +- `countDelta` - the number of messages added to the queue *since the last message - counter update* + the number of messages added to the queue *since the last message counter + update* -- `messageCount` +- `messageCount` - The *current* number of messages in the queue + The *current* number of messages in the queue -- `messageCountDelta` +- `messageCountDelta` - The *overall* number of messages added/removed from the queue *since - the last message counter update*. For example, if - `messageCountDelta` is equal to `-10` this means that overall 10 - messages have been removed from the queue (e.g. 2 messages were - added and 12 were removed) + The *overall* number of messages added/removed from the queue *since the last + message counter update*. For example, if `messageCountDelta` is equal to `-10` + this means that overall 10 messages have been removed from the queue (e.g. 2 + messages were added and 12 were removed) -- `lastAddTimestamp` +- `lastAddTimestamp` - The timestamp of the last time a message was added to the queue + The timestamp of the last time a message was added to the queue -- `udpateTimestamp` +- `udpateTimestamp` - The timestamp of the last message counter update + The timestamp of the last message counter update -These attributes can be used to determine other meaningful data as well. -For example, to know specifically how many messages were *consumed* from -the queue since the last update simply subtract the `messageCountDelta` -from `countDelta`. + These attributes can be used to determine other meaningful data as well. For + example, to know specifically how many messages were *consumed* from the queue + since the last update simply subtract the `messageCountDelta` from + `countDelta`. ### Configuring Message Counters -By default, message counters are disabled as it might have a small -negative effect on memory. +By default, message counters are disabled as it might have a small negative +effect on memory. -To enable message counters, you can set it to `true` in -`broker.xml`: +To enable message counters, you can set it to `true` in `broker.xml`: ```xml true ``` -Message counters keep a history of the queue metrics (10 days by -default) and sample all the queues at regular interval (10 seconds by -default). If message counters are enabled, these values should be -configured to suit your messaging use case in -`broker.xml`: +Message counters keep a history of the queue metrics (10 days by default) and +sample all the queues at regular interval (10 seconds by default). If message +counters are enabled, these values should be configured to suit your messaging +use case in `broker.xml`: ```xml @@ -842,8 +876,8 @@ configured to suit your messaging use case in 60000 ``` -Message counters can be retrieved using the Management API. For example, -to retrieve message counters on a queue using JMX: +Message counters can be retrieved using the Management API. For example, to +retrieve message counters on a queue using JMX: ```java // retrieve a connection to Apache ActiveMQ Artemis's MBeanServer @@ -863,4 +897,5 @@ messageCounter.getMessageCountDelta()); ### Example -See the [Examples](examples.md) chapter for an example which shows how to use message counters to retrieve information on a queue. +See the [Message Counter Example](examples.md#message-counter) which shows how +to use message counters to retrieve information on a queue. diff --git a/docs/user-manual/en/masking-passwords.md b/docs/user-manual/en/masking-passwords.md index 4ec64bae9c9..c7332c78b94 100644 --- a/docs/user-manual/en/masking-passwords.md +++ b/docs/user-manual/en/masking-passwords.md @@ -1,65 +1,67 @@ # Masking Passwords -By default all passwords in Apache ActiveMQ Artemis server's configuration files are in -plain text form. This usually poses no security issues as those files -should be well protected from unauthorized accessing. However, in some -circumstances a user doesn't want to expose its passwords to more eyes -than necessary. +By default all passwords in Apache ActiveMQ Artemis server's configuration +files are in plain text form. This usually poses no security issues as those +files should be well protected from unauthorized accessing. However, in some +circumstances a user doesn't want to expose its passwords to more eyes than +necessary. Apache ActiveMQ Artemis can be configured to use 'masked' passwords in its -configuration files. A masked password is an obscure string -representation of a real password. To mask a password a user will use an -'encoder'. The encoder takes in the real password and outputs the masked -version. A user can then replace the real password in the configuration -files with the new masked password. When Apache ActiveMQ Artemis loads a masked -password, it uses a suitable 'decoder' to decode it into real password. +configuration files. A masked password is an obscure string representation of a +real password. To mask a password a user will use an 'encoder'. The encoder +takes in the real password and outputs the masked version. A user can then +replace the real password in the configuration files with the new masked +password. When Apache ActiveMQ Artemis loads a masked password, it uses a +suitable 'decoder' to decode it into real password. -Apache ActiveMQ Artemis provides a default password encoder and decoder. Optionally -users can use or implement their own encoder and decoder for masking the -passwords. +Apache ActiveMQ Artemis provides a default password encoder and decoder. +Optionally users can use or implement their own encoder and decoder for masking +the passwords. -In general, a masked password can be identified using one of two ways. The first one -is the ENC() syntax, i.e. any string value wrapped in ENC() is to be treated as -a masked password. For example +In general, a masked password can be identified using one of two ways. The +first one is the `ENC()` syntax, i.e. any string value wrapped in `ENC()` is to +be treated as a masked password. For example `ENC(xyz)` The above indicates that the password is masked and the masked value is `xyz`. -The ENC() syntax is the preferred way to indicating a masked password and is +The `ENC()` syntax is the **preferred way** of masking a password and is universally supported in every password configuration in Artemis. -The other way is to use a `mask-password` attribute to tell that a password -in a configuration file should be treated as 'masked'. For example: +The other way is to use a `mask-password` attribute to tell that a password in +a configuration file should be treated as 'masked'. For example: ```xml true xyz ``` -This method is now deprecated and exists only to maintain backward-compatibility. -Newer configurations may not support it. + +This method is now **deprecated** and exists only to maintain +backward-compatibility. Newer configurations may not support it. ### Password Masking in Server Configuration File #### General Masking Configuration -Besides supporting the ENC() syntax, the server configuration file (i.e. broker.xml) -has a property that defines the default masking behaviors over the entire file scope. +Besides supporting the `ENC()` syntax, the server configuration file (i.e. +broker.xml) has a property that defines the default masking behaviors over the +entire file scope. -`mask-password`: this boolean type property indicates if a password -should be masked or not. Set it to "true" if you want your passwords -masked. The default value is "false". +`mask-password`: this boolean type property indicates if a password should be +masked or not. Set it to "true" if you want your passwords masked. The default +value is "false". `password-codec`: this string type property identifies the name of the class which will be used to decode the masked password within the broker. If not -specified then the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` -will be used. +specified then the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` will be used. #### Specific Masking Behaviors ##### cluster-password -If it is specified in ENC() syntax it will be treated as masked, or +If it is specified in `ENC()` syntax it will be treated as masked, or If `mask-password` is `true` the `cluster-password` will be treated as masked. @@ -73,76 +75,77 @@ will have different password masking needs. When a `connector` or `acceptor` is initialised, Apache ActiveMQ Artemis will add the aforementioned `mask-password` and `password-codec` values to the -`connector` or `acceptor` parameters using the keys `activemq.usemaskedpassword` -and `activemq.passwordcodec` respectively. The Netty and InVM implementations -will use these as needed and any other implementations will have access to -these to use if they so wish. +`connector` or `acceptor` parameters using the keys +`activemq.usemaskedpassword` and `activemq.passwordcodec` respectively. The +Netty and InVM implementations will use these as needed and any other +implementations will have access to these to use if they so wish. -The preferred way, however, is to use the ENC() syntax. +The preferred way, however, is to use the `ENC()` syntax. ##### Core Bridges -Core Bridges are configured in the server configuration file and so the -masking of its `password` properties follows the same rules as that of -`cluster-password`. It supports ENC() syntax. +Core Bridges are configured in the server configuration file and so the masking +of its `password` properties follows the same rules as that of +`cluster-password`. It supports `ENC()` syntax. -For using `mask-password` property, the following table summarizes the +For using `mask-password` property, the following table summarizes the relations among the above-mentioned properties - mask-password | cluster-password | acceptor/connector passwords | bridge password - :------------- | :---------------- | :--------------------------- | :--------------- - absent | plain text | plain text | plain text - false | plain text | plain text | plain text - true | masked | masked | masked +mask-password | cluster-password | acceptor/connector passwords | bridge password +--- | --- | --- | --- +absent|plain text|plain text|plain text +false|plain text|plain text|plain text +true|masked|masked|masked -It is recommended that you use the `ENC()` syntax for new applications/deployments. +It is recommended that you use the `ENC()` syntax for new +applications/deployments. #### Examples -Note: In the following examples if related attributed or properties are +**Note:** In the following examples if related attributed or properties are absent, it means they are not specified in the configure file. -example 1 +- Unmasked -```xml -bbc -``` + ```xml + bbc + ``` -This indicates the cluster password is a plain text value ("bbc"). + This indicates the cluster password is a plain text value `bbc`. -example 2 +- Masked 1 -```xml -ENC(xyz) -``` + ```xml + ENC(xyz) + ``` -This indicates the cluster password is a masked value ("xyz"). + This indicates the cluster password is a masked value `xyz`. -example 3 +- Masked 2 -```xml -true -80cf731af62c290 -``` + ```xml + true + 80cf731af62c290 + ``` -This indicates the cluster password is a masked value and Apache ActiveMQ Artemis will -use its built-in decoder to decode it. All other passwords in the -configuration file, Connectors, Acceptors and Bridges, will also use -masked passwords. + This indicates the cluster password is a masked value and Apache ActiveMQ + Artemis will use its built-in decoder to decode it. All other passwords in the + configuration file, Connectors, Acceptors and Bridges, will also use masked + passwords. #### Passwords in bootstrap.xml The broker embeds a web-server for hosting some web applications such as a -management console. It is configured in bootstrap.xml as a web -component. The web server can be secured using https protocol, and it can be -configured with a keystore password and/or truststore password which by -default are specified in plain text forms. +management console. It is configured in bootstrap.xml as a web component. The +web server can be secured using https protocol, and it can be configured with a +keystore password and/or truststore password which by default are specified in +plain text forms. -To mask these passwords you need to use ENC() syntax. The `mask-password` is -not supported here. +To mask these passwords you need to use `ENC()` syntax. The `mask-password` +boolean is not supported here. -You can also set the `passwordCodec` attribute if you want to use a password codec -other than the default one. For example +You can also set the `passwordCodec` attribute if you want to use a password +codec other than the default one. For example ```xml @@ -206,16 +208,17 @@ Example 2 Using the "UseMaskedPassword" property: ``` -With this configuration, both passwords in ra.xml and all of its MDBs -will have to be in masked form. +With this configuration, both passwords in ra.xml and all of its MDBs will have +to be in masked form. ### Passwords in artemis-users.properties Apache ActiveMQ Artemis's built-in security manager uses plain properties files -where the user passwords are specified in a hashed form by default. Note, the passwords -are technically *hashed* rather than masked in this context. The default `PropertiesLoginModule` -will not decode the passwords in `artemis-users.properties` but will instead hash the input -and compare the two hashed values for password verification. +where the user passwords are specified in a hashed form by default. Note, the +passwords are technically *hashed* rather than masked in this context. The +default `PropertiesLoginModule` will not decode the passwords in +`artemis-users.properties` but will instead hash the input and compare the two +hashed values for password verification. Please use Artemis CLI command to add a password. For example: @@ -223,26 +226,27 @@ Please use Artemis CLI command to add a password. For example: ./artemis user add --username guest --password guest --role admin ``` -This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` -to perform a "one-way" hash of the password and alter both the `artemis-users.properties` +This will use the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a +"one-way" hash of the password and alter both the `artemis-users.properties` and `artemis-roles.properties` files with the specified values. -Passwords in `artemis-users.properties` are automatically detected as hashed or not -by looking for the syntax `ENC()`. The `mask-password` parameter does not need -to be `true` to use hashed passwords here. +Passwords in `artemis-users.properties` are automatically detected as hashed or +not by looking for the syntax `ENC()`. The `mask-password` parameter does +not need to be `true` to use hashed passwords here. ### Password in login.config -Artemis supports LDAP login modules to be configured in JAAS configuration -file (default name is `login.config`). When connecting to a LDAP server usually -you need to supply a connection password in the config file. By default this +Artemis supports LDAP login modules to be configured in JAAS configuration file +(default name is `login.config`). When connecting to a LDAP server usually you +need to supply a connection password in the config file. By default this password is in plain text form. -To mask it you need to configure the passwords in your login module -using ENC() syntax. To specify a codec using the following property: +To mask it you need to configure the passwords in your login module using +`ENC()` syntax. To specify a codec using the following property: -`passwordCodec` - the password codec class name. (the default codec -will be used if it is absent) +`passwordCodec` - the password codec class name. (the default codec will be +used if it is absent) For example: @@ -270,24 +274,24 @@ LDAPLoginExternalPasswordCodec { ### Choosing a decoder for password masking -As described in the previous sections, all password masking requires a -decoder. A decoder uses an algorithm to convert a masked password into -its original clear text form in order to be used in various security -operations. The algorithm used for decoding must match that for -encoding. Otherwise the decoding may not be successful. +As described in the previous sections, all password masking requires a decoder. +A decoder uses an algorithm to convert a masked password into its original +clear text form in order to be used in various security operations. The +algorithm used for decoding must match that for encoding. Otherwise the +decoding may not be successful. For user's convenience Apache ActiveMQ Artemis provides a default decoder. However a user can implement their own if they wish. #### The Default Decoder -Whenever no decoder is specified in the configuration file, the default -decoder is used. The class name for the default decoder is -`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec`. It has hashing, -encoding, and decoding capabilities. It uses `java.crypto.Cipher` utilities -to hash or encode a plaintext password and also to decode a masked string using -same algorithm and key. Using this decoder/encoder is pretty straightforward. To -get a mask for a password, just run the `mask` command: +Whenever no decoder is specified in the configuration file, the default decoder +is used. The class name for the default decoder is +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec`. It has +hashing, encoding, and decoding capabilities. It uses `java.crypto.Cipher` +utilities to hash or encode a plaintext password and also to decode a masked +string using same algorithm and key. Using this decoder/encoder is pretty +straightforward. To get a mask for a password, just run the `mask` command: ```sh ./artemis mask @@ -304,18 +308,19 @@ plaintext password in broker.xml with it. #### Using a custom decoder -It is possible to use a custom decoder rather than the built-in one. -Simply make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom decoder -can also be service loaded rather than class loaded, if the decoder's service provider is installed in the classpath. -Then configure the server to use it as follows: +It is possible to use a custom decoder rather than the built-in one. Simply +make sure the decoder is in Apache ActiveMQ Artemis's classpath. The custom +decoder can also be service loaded rather than class loaded, if the decoder's +service provider is installed in the classpath. Then configure the server to +use it as follows: ```xml com.foo.SomeDecoder;key1=value1;key2=value2 ``` -If your decoder needs params passed to it you can do this via key/value -pairs when configuring. For instance if your decoder needs say a -"key-location" parameter, you can define like so: +If your decoder needs params passed to it you can do this via key/value pairs +when configuring. For instance if your decoder needs say a "key-location" +parameter, you can define like so: ```xml com.foo.NewDecoder;key-location=/some/url/to/keyfile @@ -328,15 +333,14 @@ Then configure your cluster-password like this: ``` When Apache ActiveMQ Artemis reads the cluster-password it will initialize the -NewDecoder and use it to decode "mask\_password". It also process all -passwords using the new defined decoder. +NewDecoder and use it to decode "mask\_password". It also process all passwords +using the new defined decoder. #### Implementing Custom Codecs -To use a different decoder than the built-in one, you either pick one -from existing libraries or you implement it yourself. All decoders must -implement the `org.apache.activemq.artemis.utils.SensitiveDataCodec` -interface: +To use a different decoder than the built-in one, you either pick one from +existing libraries or you implement it yourself. All decoders must implement +the `org.apache.activemq.artemis.utils.SensitiveDataCodec` interface: ```java public interface SensitiveDataCodec @@ -347,8 +351,8 @@ public interface SensitiveDataCodec } ``` -This is a generic type interface but normally for a password you just -need String type. So a new decoder would be defined like +This is a generic type interface but normally for a password you just need +String type. So a new decoder would be defined like ```java public class MyNewDecoder implements SensitiveDataCodec @@ -367,4 +371,5 @@ public class MyNewDecoder implements SensitiveDataCodec ``` Last but not least, once you get your own decoder please [add it to the -classpath](using-server.md#adding-runtime-dependencies) otherwise the broker will fail to load it! \ No newline at end of file +classpath](using-server.md#adding-runtime-dependencies) otherwise the broker +will fail to load it! diff --git a/docs/user-manual/en/maven-plugin.md b/docs/user-manual/en/maven-plugin.md index 19bda2f22bf..143db9d23b1 100644 --- a/docs/user-manual/en/maven-plugin.md +++ b/docs/user-manual/en/maven-plugin.md @@ -13,17 +13,17 @@ You could for example use these maven plugins on your testsuite or deployment au There are three goals that you can use -- create +- `create` -This will create a server accordingly to your arguments. You can do some extra tricks here such as installing extra libraries for external modules. + This will create a server accordingly to your arguments. You can do some extra tricks here such as installing extra libraries for external modules. -- cli +- `cli` -This will perform any CLI operation. This is basically a maven expression of the CLI classes + This will perform any CLI operation. This is basically a maven expression of the CLI classes -- runClient +- `runClient` -This is a simple wrapper around classes implementing a static main call. Notice that this won't spawn a new VM or new Thread. + This is a simple wrapper around classes implementing a static main call. Notice that this won't spawn a new VM or new Thread. ## Declaration @@ -31,11 +31,14 @@ This is a simple wrapper around classes implementing a static main call. Notice On your pom, use the plugins section: ```xml - - - - org.apache.activemq - artemis-maven-plugin + + + + org.apache.activemq + artemis-maven-plugin + + + ``` ## create goal @@ -43,7 +46,7 @@ On your pom, use the plugins section: I won't detail every operation of the create plugin here, but I will try to describe the main parameters: Name | Description -:--- | :--- +--- | --- configuration | A place that will hold any file to replace on the configuration. For instance if you are providing your own broker.xml. Default is "${basedir}/target/classes/activemq/server0" home | The location where you downloaded and installed artemis. Default is "${activemq.basedir}" alternateHome | This is used case you have two possible locations for your home (e.g. one under compile and one under production @@ -54,16 +57,15 @@ liblist[] | A list of libraries to be installed under ./lib. ex: "org.jgroups:jg Example: ```xml - - - create - - create - - - ${noServer} - - + + create + + create + + + ${noServer} + + ``` @@ -72,7 +74,7 @@ Example: Some properties for the CLI Name | Description -:--- | :--- +--- | --- configuration | A place that will hold any file to replace on the configuration. For instance if you are providing your own broker.xml. Default is "${basedir}/target/classes/activemq/server0" home | The location where you downloaded and installed artemis. Default is "${activemq.basedir}" alternateHome | This is used case you have two possible locations for your home (e.g. one under compile and one under production @@ -105,7 +107,7 @@ Example: This is a simple solution for running classes implementing the main method. Name | Description -:--- | :--- +--- | --- clientClass | A class implement a static void main(String arg[]) args | A string array of arguments passed to the method diff --git a/docs/user-manual/en/message-expiry.md b/docs/user-manual/en/message-expiry.md index 99cb73f1cfa..68463f591cf 100644 --- a/docs/user-manual/en/message-expiry.md +++ b/docs/user-manual/en/message-expiry.md @@ -2,19 +2,19 @@ Messages can be set with an optional *time to live* when sending them. -Apache ActiveMQ Artemis will not deliver a message to a consumer after it's time to -live has been exceeded. If the message hasn't been delivered by the time -that time to live is reached the server can discard it. +Apache ActiveMQ Artemis will not deliver a message to a consumer after it's +time to live has been exceeded. If the message hasn't been delivered by the +time that time to live is reached the server can discard it. -Apache ActiveMQ Artemis's addresses can be assigned a expiry address so that, when -messages are expired, they are removed from the queue and sent to the -expiry address. Many different queues can be bound to an expiry address. -These *expired* messages can later be consumed for further inspection. +Apache ActiveMQ Artemis's addresses can be assigned a expiry address so that, +when messages are expired, they are removed from the queue and sent to the +expiry address. Many different queues can be bound to an expiry address. These +*expired* messages can later be consumed for further inspection. ## Core API -Using Apache ActiveMQ Artemis Core API, you can set an expiration time directly on the -message: +Using Apache ActiveMQ Artemis Core API, you can set an expiration time directly +on the message: ```java // message will expire in 5000ms from now @@ -28,23 +28,23 @@ JMS MessageProducer allows to set a TimeToLive for the messages it sent: producer.setTimeToLive(5000); ``` -Expired messages which are consumed from an expiry address have the -following properties: +Expired messages which are consumed from an expiry address have the following +properties: -- `_AMQ_ORIG_ADDRESS` +- `_AMQ_ORIG_ADDRESS` - a String property containing the *original address* of the expired - message + a String property containing the *original address* of the expired + message -- `_AMQ_ORIG_QUEUE` +- `_AMQ_ORIG_QUEUE` - a String property containing the *original queue* of the expired - message + a String property containing the *original queue* of the expired + message -- `_AMQ_ACTUAL_EXPIRY` +- `_AMQ_ACTUAL_EXPIRY` - a Long property containing the *actual expiration time* of the - expired message + a Long property containing the *actual expiration time* of the + expired message ## Configuring Expiry Addresses @@ -57,29 +57,29 @@ Expiry address are defined in the address-setting configuration:
``` -If messages are expired and no expiry address is specified, messages are -simply removed from the queue and dropped. Address wildcards can be used -to configure expiry address for a set of addresses (see [Understanding the Wildcard Syntax](wildcard-syntax.md)). +If messages are expired and no expiry address is specified, messages are simply +removed from the queue and dropped. Address [wildcards](wildcard-syntax.md) can +be used to configure expiry address for a set of addresses. ## Configuring The Expiry Reaper Thread -A reaper thread will periodically inspect the queues to check if -messages have expired. +A reaper thread will periodically inspect the queues to check if messages have +expired. The reaper thread can be configured with the following properties in `broker.xml` -- `message-expiry-scan-period` +- `message-expiry-scan-period` - How often the queues will be scanned to detect expired messages (in - milliseconds, default is 30000ms, set to `-1` to disable the reaper - thread) + How often the queues will be scanned to detect expired messages (in + milliseconds, default is 30000ms, set to `-1` to disable the reaper thread) -- `message-expiry-thread-priority` +- `message-expiry-thread-priority` - The reaper thread priority (it must be between 1 and 10, 10 being the - highest priority, default is 3) + The reaper thread priority (it must be between 1 and 10, 10 being the highest + priority, default is 3) ## Example -See the [examples.md](examples.md) chapter for an example which shows how message expiry is configured and used with JMS. +See the [Message Expiration Example](examples.md#message-expiration) which +shows how message expiry is configured and used with JMS. diff --git a/docs/user-manual/en/message-grouping.md b/docs/user-manual/en/message-grouping.md index 5fb42552472..050832747fa 100644 --- a/docs/user-manual/en/message-grouping.md +++ b/docs/user-manual/en/message-grouping.md @@ -1,106 +1,106 @@ # Message Grouping -Message groups are sets of messages that have the following -characteristics: +Message groups are sets of messages that have the following characteristics: -- Messages in a message group share the same group id, i.e. they have - same group identifier property (`JMSXGroupID` for JMS, - `_AMQ_GROUP_ID` for Apache ActiveMQ Artemis Core API). +- Messages in a message group share the same group id, i.e. they have same + group identifier property (`JMSXGroupID` for JMS, `_AMQ_GROUP_ID` for Apache + ActiveMQ Artemis Core API). -- Messages in a message group are always consumed by the same - consumer, even if there are many consumers on a queue. They pin all - messages with the same group id to the same consumer. If that - consumer closes another consumer is chosen and will receive all - messages with the same group id. +- Messages in a message group are always consumed by the same consumer, even if + there are many consumers on a queue. They pin all messages with the same + group id to the same consumer. If that consumer closes another consumer is + chosen and will receive all messages with the same group id. -Message groups are useful when you want all messages for a certain value -of the property to be processed serially by the same consumer. +Message groups are useful when you want all messages for a certain value of the +property to be processed serially by the same consumer. -An example might be orders for a certain stock. You may want orders for -any particular stock to be processed serially by the same consumer. To -do this you can create a pool of consumers (perhaps one for each stock, -but less will work too), then set the stock name as the value of the -_AMQ_GROUP_ID property. +An example might be orders for a certain stock. You may want orders for any +particular stock to be processed serially by the same consumer. To do this you +can create a pool of consumers (perhaps one for each stock, but less will work +too), then set the stock name as the value of the _AMQ_GROUP_ID property. This will ensure that all messages for a particular stock will always be processed by the same consumer. -> **Note** +> **Note:** > -> Grouped messages can impact the concurrent processing of non-grouped -> messages due to the underlying FIFO semantics of a queue. For example, -> if there is a chunk of 100 grouped messages at the head of a queue -> followed by 1,000 non-grouped messages then all the grouped messages -> will need to be sent to the appropriate client (which is consuming -> those grouped messages serially) before any of the non-grouped -> messages can be consumed. The functional impact in this scenario is a -> temporary suspension of concurrent message processing while all the -> grouped messages are processed. This can be a performance bottleneck -> so keep it in mind when determining the size of your message groups, -> and consider whether or not you should isolate your grouped messages +> Grouped messages can impact the concurrent processing of non-grouped messages +> due to the underlying FIFO semantics of a queue. For example, if there is a +> chunk of 100 grouped messages at the head of a queue followed by 1,000 +> non-grouped messages then all the grouped messages will need to be sent to +> the appropriate client (which is consuming those grouped messages serially) +> before any of the non-grouped messages can be consumed. The functional impact +> in this scenario is a temporary suspension of concurrent message processing +> while all the grouped messages are processed. This can be a performance +> bottleneck so keep it in mind when determining the size of your message +> groups, and consider whether or not you should isolate your grouped messages > from your non-grouped messages. ## Using Core API -The property name used to identify the message group is `"_AMQ_GROUP_ID"` -(or the constant `MessageImpl.HDR_GROUP_ID`). Alternatively, you can set -`autogroup` to true on the `SessionFactory` which will pick a random -unique id. +The property name used to identify the message group is `"_AMQ_GROUP_ID"` (or +the constant `MessageImpl.HDR_GROUP_ID`). Alternatively, you can set +`autogroup` to true on the `SessionFactory` which will pick a random unique id. ## Using JMS The property name used to identify the message group is `JMSXGroupID`. - // send 2 messages in the same group to ensure the same - // consumer will receive both - Message message = ... - message.setStringProperty("JMSXGroupID", "Group-0"); - producer.send(message); +```java +// send 2 messages in the same group to ensure the same +// consumer will receive both +Message message = ... +message.setStringProperty("JMSXGroupID", "Group-0"); +producer.send(message); - message = ... - message.setStringProperty("JMSXGroupID", "Group-0"); - producer.send(message); +message = ... +message.setStringProperty("JMSXGroupID", "Group-0"); +producer.send(message); +``` Alternatively, you can set `autogroup` to true on the -`ActiveMQConnectonFactory` which will pick a random unique id. This can -also be set in the JNDI context environment, e.g. `jndi.properties`. -Here's a simple example using the "ConnectionFactory" connection factory -which is available in the context by default - - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.myConnectionFactory=tcp://localhost:61616?autoGroup=true +`ActiveMQConnectonFactory` which will pick a random unique id. This can also be +set in the JNDI context environment, e.g. `jndi.properties`. Here's a simple +example using the "ConnectionFactory" connection factory which is available in +the context by default + +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.myConnectionFactory=tcp://localhost:61616?autoGroup=true +``` -Alternatively you can set the group id via the connection factory. All -messages sent with producers created via this connection factory will -set the `JMSXGroupID` to the specified value on all messages sent. This -can also be set in the JNDI context environment, e.g. `jndi.properties`. -Here's a simple example using the "ConnectionFactory" connection factory -which is available in the context by default: +Alternatively you can set the group id via the connection factory. All messages +sent with producers created via this connection factory will set the +`JMSXGroupID` to the specified value on all messages sent. This can also be set +in the JNDI context environment, e.g. `jndi.properties`. Here's a simple +example using the "ConnectionFactory" connection factory which is available in +the context by default: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.myConnectionFactory=tcp://localhost:61616?groupID=Group-0 +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.myConnectionFactory=tcp://localhost:61616?groupID=Group-0 +``` ## Example -See the [examples](examples.md} chapter for an example which shows how message groups are configured and used with JMS and via a connection factory. +See the [Message Group Example](examples.md#message-group) which shows how +message groups are configured and used with JMS and via a connection factory. ## Clustered Grouping Using message groups in a cluster is a bit more complex. This is because -messages with a particular group id can arrive on any node so each node -needs to know about which group id's are bound to which consumer on -which node. The consumer handling messages for a particular group id may -be on a different node of the cluster, so each node needs to know this -information so it can route the message correctly to the node which has -that consumer. +messages with a particular group id can arrive on any node so each node needs +to know about which group id's are bound to which consumer on which node. The +consumer handling messages for a particular group id may be on a different node +of the cluster, so each node needs to know this information so it can route the +message correctly to the node which has that consumer. -To solve this there is the notion of a grouping handler. Each node will -have its own grouping handler and when a messages is sent with a group -id assigned, the handlers will decide between them which route the -message should take. +To solve this there is the notion of a grouping handler. Each node will have +its own grouping handler and when a messages is sent with a group id assigned, +the handlers will decide between them which route the message should take. -Here is a sample config for each type of handler. This should be -configured in `broker.xml`. +Here is a sample config for each type of handler. This should be configured in +`broker.xml`. ```xml @@ -116,71 +116,66 @@ configured in `broker.xml`. ``` - - `type` two types of handlers are supported - `LOCAL` and `REMOTE`. - Each cluster should choose 1 node to have a `LOCAL` grouping handler - and all the other nodes should have `REMOTE` handlers. It's the `LOCAL` - handler that actually makes the decision as to what route should be - used, all the other `REMOTE` handlers converse with this. - - - `address` refers to a [cluster connection and the address - it uses](clusters.md#configuring-cluster-connections). Refer to the - clustering section on how to configure clusters. +- `type` two types of handlers are supported - `LOCAL` and `REMOTE`. Each + cluster should choose 1 node to have a `LOCAL` grouping handler and all the + other nodes should have `REMOTE` handlers. It's the `LOCAL` handler that + actually makes the decision as to what route should be used, all the other + `REMOTE` handlers converse with this. + +- `address` refers to a [cluster connection and the address it + uses](clusters.md#configuring-cluster-connections). Refer to the clustering + section on how to configure clusters. - - `timeout` how long to wait for a decision to be made. An exception - will be thrown during the send if this timeout is reached, this - ensures that strict ordering is kept. - -The decision as to where a message should be routed to is initially -proposed by the node that receives the message. The node will pick a -suitable route as per the normal clustered routing conditions, i.e. -round robin available queues, use a local queue first and choose a queue -that has a consumer. If the proposal is accepted by the grouping -handlers the node will route messages to this queue from that point on, -if rejected an alternative route will be offered and the node will again -route to that queue indefinitely. All other nodes will also route to the -queue chosen at proposal time. Once the message arrives at the queue -then normal single server message group semantics take over and the +- `timeout` how long to wait for a decision to be made. An exception will be + thrown during the send if this timeout is reached, this ensures that strict + ordering is kept. + +The decision as to where a message should be routed to is initially proposed by +the node that receives the message. The node will pick a suitable route as per +the normal clustered routing conditions, i.e. round robin available queues, +use a local queue first and choose a queue that has a consumer. If the proposal +is accepted by the grouping handlers the node will route messages to this queue +from that point on, if rejected an alternative route will be offered and the +node will again route to that queue indefinitely. All other nodes will also +route to the queue chosen at proposal time. Once the message arrives at the +queue then normal single server message group semantics take over and the message is pinned to a consumer on that queue. -You may have noticed that there is a single point of failure with the -single local handler. If this node crashes then no decisions will be -able to be made. Any messages sent will be not be delivered and an -exception thrown. To avoid this happening Local Handlers can be -replicated on another backup node. Simple create your back up node and -configure it with the same Local handler. +You may have noticed that there is a single point of failure with the single +local handler. If this node crashes then no decisions will be able to be made. +Any messages sent will be not be delivered and an exception thrown. To avoid +this happening Local Handlers can be replicated on another backup node. Simple +create your back up node and configure it with the same Local handler. ## Clustered Grouping Best Practices Some best practices should be followed when using clustered grouping: -1. Make sure your consumers are distributed evenly across the different - nodes if possible. This is only an issue if you are creating and - closing consumers regularly. Since messages are always routed to the - same queue once pinned, removing a consumer from this queue may - leave it with no consumers meaning the queue will just keep - receiving the messages. Avoid closing consumers or make sure that - you always have plenty of consumers, i.e., if you have 3 nodes have - 3 consumers. - -2. Use durable queues if possible. If queues are removed once a group - is bound to it, then it is possible that other nodes may still try - to route messages to it. This can be avoided by making sure that the - queue is deleted by the session that is sending the messages. This - means that when the next message is sent it is sent to the node - where the queue was deleted meaning a new proposal can successfully - take place. Alternatively you could just start using a different - group id. - -3. Always make sure that the node that has the Local Grouping Handler - is replicated. These means that on failover grouping will still - occur. - -4. In case you are using group-timeouts, the remote node should have a - smaller group-timeout with at least half of the value on the main - coordinator. This is because this will determine how often the - last-time-use value should be updated with a round trip for a - request to the group between the nodes. +1. Make sure your consumers are distributed evenly across the different nodes + if possible. This is only an issue if you are creating and closing + consumers regularly. Since messages are always routed to the same queue once + pinned, removing a consumer from this queue may leave it with no consumers + meaning the queue will just keep receiving the messages. Avoid closing + consumers or make sure that you always have plenty of consumers, i.e., if you + have 3 nodes have 3 consumers. + +2. Use durable queues if possible. If queues are removed once a group is bound + to it, then it is possible that other nodes may still try to route messages + to it. This can be avoided by making sure that the queue is deleted by the + session that is sending the messages. This means that when the next message is + sent it is sent to the node where the queue was deleted meaning a new proposal + can successfully take place. Alternatively you could just start using a + different group id. + +3. Always make sure that the node that has the Local Grouping Handler is + replicated. These means that on failover grouping will still occur. + +4. In case you are using group-timeouts, the remote node should have a smaller + group-timeout with at least half of the value on the main coordinator. This + is because this will determine how often the last-time-use value should be + updated with a round trip for a request to the group between the nodes. ## Clustered Grouping Example -See the [examples](examples.md) chapter for an example of how to configure message groups with a ActiveMQ Artemis Cluster. +See the [Clustered Grouping Example](examples.md#clustered-grouping) which +shows how to configure message groups with a ActiveMQ Artemis Cluster. diff --git a/docs/user-manual/en/messaging-concepts.md b/docs/user-manual/en/messaging-concepts.md index 582b85d754d..ab829cccdf0 100644 --- a/docs/user-manual/en/messaging-concepts.md +++ b/docs/user-manual/en/messaging-concepts.md @@ -1,240 +1,240 @@ # Messaging Concepts -Apache ActiveMQ Artemis is an asynchronous messaging system, an example of [Message -Oriented -Middleware](https://en.wikipedia.org/wiki/Message-oriented_middleware) , -we'll just call them messaging systems in the remainder of this book. +Apache ActiveMQ Artemis is an asynchronous messaging system, an example of +[Message Oriented +Middleware](https://en.wikipedia.org/wiki/Message-oriented_middleware) , we'll +just call them messaging systems in the remainder of this book. -We'll first present a brief overview of what kind of things messaging -systems do, where they're useful and the kind of concepts you'll hear -about in the messaging world. +We'll first present a brief overview of what kind of things messaging systems +do, where they're useful and the kind of concepts you'll hear about in the +messaging world. If you're already familiar with what a messaging system is and what it's capable of, then you can skip this chapter. ## General Concepts -Messaging systems allow you to loosely couple heterogeneous systems -together, whilst typically providing reliability, transactions and many -other features. +Messaging systems allow you to loosely couple heterogeneous systems together, +whilst typically providing reliability, transactions and many other features. Unlike systems based on a [Remote Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call) (RPC) pattern, -messaging systems primarily use an asynchronous message passing pattern -with no tight relationship between requests and responses. Most -messaging systems also support a request-response mode but this is not a -primary feature of messaging systems. - -Designing systems to be asynchronous from end-to-end allows you to -really take advantage of your hardware resources, minimizing the amount -of threads blocking on IO operations, and to use your network bandwidth -to its full capacity. With an RPC approach you have to wait for a -response for each request you make so are limited by the network round -trip time, or *latency* of your network. With an asynchronous system you -can pipeline flows of messages in different directions, so are limited -by the network *bandwidth* not the latency. This typically allows you to -create much higher performance applications. +messaging systems primarily use an asynchronous message passing pattern with no +tight relationship between requests and responses. Most messaging systems also +support a request-response mode but this is not a primary feature of messaging +systems. + +Designing systems to be asynchronous from end-to-end allows you to really take +advantage of your hardware resources, minimizing the amount of threads blocking +on IO operations, and to use your network bandwidth to its full capacity. With +an RPC approach you have to wait for a response for each request you make so +are limited by the network round trip time, or *latency* of your network. With +an asynchronous system you can pipeline flows of messages in different +directions, so are limited by the network *bandwidth* not the latency. This +typically allows you to create much higher performance applications. Messaging systems decouple the senders of messages from the consumers of -messages. The senders and consumers of messages are completely -independent and know nothing of each other. This allows you to create -flexible, loosely coupled systems. - -Often, large enterprises use a messaging system to implement a message -bus which loosely couples heterogeneous systems together. Message buses -often form the core of an [Enterprise Service -Bus](https://en.wikipedia.org/wiki/Enterprise_service_bus). (ESB). Using -a message bus to de-couple disparate systems can allow the system to -grow and adapt more easily. It also allows more flexibility to add new -systems or retire old ones since they don't have brittle dependencies on -each other. +messages. The senders and consumers of messages are completely independent and +know nothing of each other. This allows you to create flexible, loosely coupled +systems. + +Often, large enterprises use a messaging system to implement a message bus +which loosely couples heterogeneous systems together. Message buses often form +the core of an [Enterprise Service +Bus](https://en.wikipedia.org/wiki/Enterprise_service_bus). (ESB). Using a +message bus to de-couple disparate systems can allow the system to grow and +adapt more easily. It also allows more flexibility to add new systems or retire +old ones since they don't have brittle dependencies on each other. ## Messaging styles -Messaging systems normally support two main styles of asynchronous -messaging: [message queue](https://en.wikipedia.org/wiki/Message_queue) -messaging (also known as *point-to-point messaging*) and [publish -subscribe](https://en.wikipedia.org/wiki/Publish_subscribe) messaging. -We'll summarise them briefly here: +Messaging systems normally support two main styles of asynchronous messaging: +[message queue](https://en.wikipedia.org/wiki/Message_queue) messaging (also +known as *point-to-point messaging*) and [publish +subscribe](https://en.wikipedia.org/wiki/Publish_subscribe) messaging. We'll +summarise them briefly here: ### Point-to-Point -With this type of messaging you send a message to a queue. The message -is then typically persisted to provide a guarantee of delivery, then -some time later the messaging system delivers the message to a consumer. -The consumer then processes the message and when it is done, it -acknowledges the message. Once the message is acknowledged it disappears -from the queue and is not available to be delivered again. If the system -crashes before the messaging server receives an acknowledgement from the -consumer, then on recovery, the message will be available to be -delivered to a consumer again. - -With point-to-point messaging, there can be many consumers on the queue -but a particular message will only ever be consumed by a maximum of one -of them. Senders (also known as *producers*) to the queue are completely -decoupled from receivers (also known as *consumers*) of the queue - they -do not know of each other's existence. - -A classic example of point to point messaging would be an order queue in -a company's book ordering system. Each order is represented as a message -which is sent to the order queue. Let's imagine there are many front end -ordering systems which send orders to the order queue. When a message -arrives on the queue it is persisted - this ensures that if the server -crashes the order is not lost. Let's also imagine there are many -consumers on the order queue - each representing an instance of an order -processing component - these can be on different physical machines but -consuming from the same queue. The messaging system delivers each -message to one and only one of the ordering processing components. -Different messages can be processed by different order processors, but a -single order is only processed by one order processor - this ensures +With this type of messaging you send a message to a queue. The message is then +typically persisted to provide a guarantee of delivery, then some time later +the messaging system delivers the message to a consumer. The consumer then +processes the message and when it is done, it acknowledges the message. Once +the message is acknowledged it disappears from the queue and is not available +to be delivered again. If the system crashes before the messaging server +receives an acknowledgement from the consumer, then on recovery, the message +will be available to be delivered to a consumer again. + +With point-to-point messaging, there can be many consumers on the queue but a +particular message will only ever be consumed by a maximum of one of them. +Senders (also known as *producers*) to the queue are completely decoupled from +receivers (also known as *consumers*) of the queue - they do not know of each +other's existence. + +A classic example of point to point messaging would be an order queue in a +company's book ordering system. Each order is represented as a message which is +sent to the order queue. Let's imagine there are many front end ordering +systems which send orders to the order queue. When a message arrives on the +queue it is persisted - this ensures that if the server crashes the order is +not lost. Let's also imagine there are many consumers on the order queue - each +representing an instance of an order processing component - these can be on +different physical machines but consuming from the same queue. The messaging +system delivers each message to one and only one of the ordering processing +components. Different messages can be processed by different order processors, +but a single order is only processed by one order processor - this ensures orders aren't processed twice. -As an order processor receives a message, it fulfills the order, sends -order information to the warehouse system and then updates the order -database with the order details. Once it's done that it acknowledges the -message to tell the server that the order has been processed and can be -forgotten about. Often the send to the warehouse system, update in -database and acknowledgement will be completed in a single transaction -to ensure [ACID](https://en.wikipedia.org/wiki/ACID) properties. +As an order processor receives a message, it fulfills the order, sends order +information to the warehouse system and then updates the order database with +the order details. Once it's done that it acknowledges the message to tell the +server that the order has been processed and can be forgotten about. Often the +send to the warehouse system, update in database and acknowledgement will be +completed in a single transaction to ensure +[ACID](https://en.wikipedia.org/wiki/ACID) properties. ### Publish-Subscribe -With publish-subscribe messaging many senders can send messages to an -entity on the server, often called a *topic* (e.g. in the JMS world). +With publish-subscribe messaging many senders can send messages to an entity on +the server, often called a *topic* (e.g. in the JMS world). -There can be many *subscriptions* on a topic, a subscription is just -another word for a consumer of a topic. Each subscription receives a -*copy* of *each* message sent to the topic. This differs from the -message queue pattern where each message is only consumed by a single -consumer. +There can be many *subscriptions* on a topic, a subscription is just another +word for a consumer of a topic. Each subscription receives a *copy* of *each* +message sent to the topic. This differs from the message queue pattern where +each message is only consumed by a single consumer. -Subscriptions can optionally be *durable* which means they retain a copy -of each message sent to the topic until the subscriber consumes them - -even if the server crashes or is restarted in between. Non-durable -subscriptions only last a maximum of the lifetime of the connection that -created them. +Subscriptions can optionally be *durable* which means they retain a copy of +each message sent to the topic until the subscriber consumes them - even if the +server crashes or is restarted in between. Non-durable subscriptions only last +a maximum of the lifetime of the connection that created them. An example of publish-subscribe messaging would be a news feed. As news -articles are created by different editors around the world they are sent -to a news feed topic. There are many subscribers around the world who -are interested in receiving news items - each one creates a subscription -and the messaging system ensures that a copy of each news message is -delivered to each subscription. +articles are created by different editors around the world they are sent to a +news feed topic. There are many subscribers around the world who are interested +in receiving news items - each one creates a subscription and the messaging +system ensures that a copy of each news message is delivered to each +subscription. ## Delivery guarantees -A key feature of most messaging systems is *reliable messaging*. With -reliable messaging the server gives a guarantee that the message will be -delivered once and only once to each consumer of a queue or each durable -subscription of a topic, even in the event of system failure. This is -crucial for many businesses; e.g. you don't want your orders fulfilled -more than once or any of your orders to be lost. +A key feature of most messaging systems is *reliable messaging*. With reliable +messaging the server gives a guarantee that the message will be delivered once +and only once to each consumer of a queue or each durable subscription of a +topic, even in the event of system failure. This is crucial for many +businesses; e.g. you don't want your orders fulfilled more than once or any of +your orders to be lost. -In other cases you may not care about a once and only once delivery -guarantee and are happy to cope with duplicate deliveries or lost -messages - an example of this might be transient stock price updates - -which are quickly superseded by the next update on the same stock. The -messaging system allows you to configure which delivery guarantees you -require. +In other cases you may not care about a once and only once delivery guarantee +and are happy to cope with duplicate deliveries or lost messages - an example +of this might be transient stock price updates - which are quickly superseded +by the next update on the same stock. The messaging system allows you to +configure which delivery guarantees you require. ## Transactions -Messaging systems typically support the sending and acknowledgement of -multiple messages in a single local transaction. Apache ActiveMQ Artemis also supports +Messaging systems typically support the sending and acknowledgement of multiple +messages in a single local transaction. Apache ActiveMQ Artemis also supports the sending and acknowledgement of message as part of a large global transaction - using the Java mapping of XA: JTA. ## Durability -Messages are either durable or non durable. Durable messages will be -persisted in permanent storage and will survive server failure or -restart. Non durable messages will not survive server failure or -restart. Examples of durable messages might be orders or trades, where -they cannot be lost. An example of a non durable message might be a -stock price update which is transitory and doesn't need to survive a -restart. +Messages are either durable or non durable. Durable messages will be persisted +in permanent storage and will survive server failure or restart. Non durable +messages will not survive server failure or restart. Examples of durable +messages might be orders or trades, where they cannot be lost. An example of a +non durable message might be a stock price update which is transitory and +doesn't need to survive a restart. ## Messaging APIs and protocols -How do client applications interact with messaging systems in order to -send and consume messages? +How do client applications interact with messaging systems in order to send and +consume messages? -Several messaging systems provide their own proprietary APIs with which -the client communicates with the messaging system. +Several messaging systems provide their own proprietary APIs with which the +client communicates with the messaging system. -There are also some standard ways of operating with messaging systems -and some emerging standards in this space. +There are also some standard ways of operating with messaging systems and some +emerging standards in this space. Let's take a brief look at these: ### Java Message Service (JMS) -[JMS](https://en.wikipedia.org/wiki/Java_Message_Service) is part of -Oracle's Java EE specification. It's a Java API that encapsulates both message -queue and publish-subscribe messaging patterns. JMS is a lowest common -denominator specification - i.e. it was created to encapsulate common -functionality of the already existing messaging systems that were -available at the time of its creation. +[JMS](https://en.wikipedia.org/wiki/Java_Message_Service) is part of Oracle's +Java EE specification. It's a Java API that encapsulates both message queue and +publish-subscribe messaging patterns. JMS is a lowest common denominator +specification - i.e. it was created to encapsulate common functionality of the +already existing messaging systems that were available at the time of its +creation. -JMS is a very popular API and is implemented by most messaging systems. -JMS is only available to clients running Java. +JMS is a very popular API and is implemented by most messaging systems. JMS is +only available to clients running Java. -JMS does not define a standard wire format - it only defines a -programmatic API so JMS clients and servers from different vendors -cannot directly interoperate since each will use the vendor's own -internal wire protocol. +JMS does not define a standard wire format - it only defines a programmatic API +so JMS clients and servers from different vendors cannot directly interoperate +since each will use the vendor's own internal wire protocol. -Apache ActiveMQ Artemis provides a fully compliant JMS 1.1 and JMS 2.0 API. +Apache ActiveMQ Artemis provides a fully compliant [JMS 1.1 and JMS 2.0 client +implementation](using-jms.md). ### System specific APIs -Many systems provide their own programmatic API for which to interact -with the messaging system. The advantage of this it allows the full set -of system functionality to be exposed to the client application. API's -like JMS are not normally rich enough to expose all the extra features -that most messaging systems provide. +Many systems provide their own programmatic API for which to interact with the +messaging system. The advantage of this it allows the full set of system +functionality to be exposed to the client application. API's like JMS are not +normally rich enough to expose all the extra features that most messaging +systems provide. -Apache ActiveMQ Artemis provides its own core client API for clients to use if they -wish to have access to functionality over and above that accessible via +Apache ActiveMQ Artemis provides its own core client API for clients to use if +they wish to have access to functionality over and above that accessible via the JMS API. +Please see [Core](core.md) for using the Core API with Apache ActiveMQ Artemis. + ### RESTful API [REST](https://en.wikipedia.org/wiki/Representational_State_Transfer) approaches to messaging are showing a lot interest recently. -It seems plausible that API standards for cloud computing may converge -on a REST style set of interfaces and consequently a REST messaging -approach is a very strong contender for becoming the de-facto method for -messaging interoperability. +It seems plausible that API standards for cloud computing may converge on a +REST style set of interfaces and consequently a REST messaging approach is a +very strong contender for becoming the de-facto method for messaging +interoperability. -With a REST approach messaging resources are manipulated as resources -defined by a URI and typically using a simple set of operations on those -resources, e.g. PUT, POST, GET etc. REST approaches to messaging often -use HTTP as their underlying protocol. +With a REST approach messaging resources are manipulated as resources defined +by a URI and typically using a simple set of operations on those resources, +e.g. PUT, POST, GET etc. REST approaches to messaging often use HTTP as their +underlying protocol. -The advantage of a REST approach with HTTP is in its simplicity and the -fact the internet is already tuned to deal with HTTP optimally. +The advantage of a REST approach with HTTP is in its simplicity and the fact +the internet is already tuned to deal with HTTP optimally. -Please see [Rest Interface](rest.md) for using Apache ActiveMQ Artemis's RESTful interface. +Please see [Rest Interface](rest.md) for using Apache ActiveMQ Artemis's +RESTful interface. ### AMQP -[AMQP](https://en.wikipedia.org/wiki/AMQP) is a specification for -interoperable messaging. It also defines a wire format, so any AMQP -client can work with any messaging system that supports AMQP. AMQP -clients are available in many different programming languages. +[AMQP](https://en.wikipedia.org/wiki/AMQP) is a specification for interoperable +messaging. It also defines a wire format, so any AMQP client can work with any +messaging system that supports AMQP. AMQP clients are available in many +different programming languages. Apache ActiveMQ Artemis implements the [AMQP 1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) -specification. Any client that supports the 1.0 specification will be -able to interact with Apache ActiveMQ Artemis. +specification. Any client that supports the 1.0 specification will be able to +interact with Apache ActiveMQ Artemis. + +Please see [AMQP](amqp.md) for using AMQP with Apache ActiveMQ Artemis. ### MQTT -[MQTT](https://mqtt.org/) is a lightweight connectivity protocol. It is designed -to run in environments where device and networks are constrained. Out of the box -Apache ActiveMQ Artemis supports version MQTT 3.1.1. Any client supporting this -version of the protocol will work against Apache ActiveMQ Artemis. + +[MQTT](https://mqtt.org/) is a lightweight connectivity protocol. It is +designed to run in environments where device and networks are constrained. Out +of the box Apache ActiveMQ Artemis supports version MQTT 3.1.1. Any client +supporting this version of the protocol will work against Apache ActiveMQ +Artemis. + +Please see [MQTT](mqtt.md) for using MQTT with Apache ActiveMQ Artemis. ### STOMP @@ -244,64 +244,67 @@ theoretically any Stomp client can work with any messaging system that supports Stomp. Stomp clients are available in many different programming languages. -Please see [Stomp](protocols-interoperability.md) for using STOMP with Apache ActiveMQ Artemis. +Please see [Stomp](stomp.md) for using STOMP with Apache ActiveMQ Artemis. + +### OpenWire -### OPENWIRE +ActiveMQ 5.x defines it's own wire protocol: OpenWire. In order to support +ActiveMQ 5.x clients, Apache ActiveMQ Artemis supports OpenWire. Any ActiveMQ +5.12.x or higher can be used with Apache ActiveMQ Artemis. -ActiveMQ 5.x defines it's own wire Protocol "OPENWIRE". In order to support -ActiveMQ 5.x clients, Apache ActiveMQ Artemis supports OPENWIRE. Any ActiveMQ 5.12.x -or higher can be used with Apache ActiveMQ Artemis. +Please see [OpenWire](openwire.md) for using OpenWire with Apache ActiveMQ +Artemis. ## High Availability -High Availability (HA) means that the system should remain operational -after failure of one or more of the servers. The degree of support for -HA varies between various messaging systems. +High Availability (HA) means that the system should remain operational after +failure of one or more of the servers. The degree of support for HA varies +between various messaging systems. Apache ActiveMQ Artemis provides automatic failover where your sessions are -automatically reconnected to the backup server on event of live server -failure. +automatically reconnected to the backup server on event of live server failure. For more information on HA, please see [High Availability and Failover](ha.md). ## Clusters -Many messaging systems allow you to create groups of messaging servers -called *clusters*. Clusters allow the load of sending and consuming -messages to be spread over many servers. This allows your system to -scale horizontally by adding new servers to the cluster. +Many messaging systems allow you to create groups of messaging servers called +*clusters*. Clusters allow the load of sending and consuming messages to be +spread over many servers. This allows your system to scale horizontally by +adding new servers to the cluster. -Degrees of support for clusters varies between messaging systems, with -some systems having fairly basic clusters with the cluster members being -hardly aware of each other. +Degrees of support for clusters varies between messaging systems, with some +systems having fairly basic clusters with the cluster members being hardly +aware of each other. -Apache ActiveMQ Artemis provides very configurable state-of-the-art clustering model -where messages can be intelligently load balanced between the servers in -the cluster, according to the number of consumers on each node, and -whether they are ready for messages. +Apache ActiveMQ Artemis provides very configurable state-of-the-art clustering +model where messages can be intelligently load balanced between the servers in +the cluster, according to the number of consumers on each node, and whether +they are ready for messages. -Apache ActiveMQ Artemis also has the ability to automatically redistribute messages -between nodes of a cluster to prevent starvation on any particular node. +Apache ActiveMQ Artemis also has the ability to automatically redistribute +messages between nodes of a cluster to prevent starvation on any particular +node. For full details on clustering, please see [Clusters](clusters.md). ## Bridges and routing -Some messaging systems allow isolated clusters or single nodes to be -bridged together, typically over unreliable connections like a wide area -network (WAN), or the internet. +Some messaging systems allow isolated clusters or single nodes to be bridged +together, typically over unreliable connections like a wide area network (WAN), +or the internet. -A bridge normally consumes from a queue on one server and forwards -messages to another queue on a different server. Bridges cope with -unreliable connections, automatically reconnecting when the connections -becomes available again. +A bridge normally consumes from a queue on one server and forwards messages to +another queue on a different server. Bridges cope with unreliable connections, +automatically reconnecting when the connections becomes available again. -Apache ActiveMQ Artemis bridges can be configured with filter expressions to only -forward certain messages, and transformation can also be hooked in. +Apache ActiveMQ Artemis bridges can be configured with filter expressions to +only forward certain messages, and transformation can also be hooked in. -Apache ActiveMQ Artemis also allows routing between queues to be configured in server -side configuration. This allows complex routing networks to be set up -forwarding or copying messages from one destination to another, forming -a global network of interconnected brokers. +Apache ActiveMQ Artemis also allows routing between queues to be configured in +server side configuration. This allows complex routing networks to be set up +forwarding or copying messages from one destination to another, forming a +global network of interconnected brokers. -For more information please see [Core Bridges](core-bridges.md) and [Diverting and Splitting Message Flows](diverts.md). +For more information please see [Core Bridges](core-bridges.md) and [Diverting +and Splitting Message Flows](diverts.md). diff --git a/docs/user-manual/en/mqtt.md b/docs/user-manual/en/mqtt.md new file mode 100644 index 00000000000..dfb8da0104c --- /dev/null +++ b/docs/user-manual/en/mqtt.md @@ -0,0 +1,137 @@ +# MQTT + +MQTT is a light weight, client to server, publish / subscribe messaging +protocol. MQTT has been specifically designed to reduce transport overhead +(and thus network traffic) and code footprint on client devices. For this +reason MQTT is ideally suited to constrained devices such as sensors and +actuators and is quickly becoming the defacto standard communication protocol +for IoT. + +Apache ActiveMQ Artemis supports MQTT v3.1.1 (and also the older v3.1 code +message format). By default there are `acceptor` elements configured to accept +MQTT connections on ports `61616` and `1883`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for MQTT. + +The best source of information on the MQTT protocol is in the [3.1.1 +specification](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html). + +Refer to the MQTT examples for a look at some of this functionality in action. + +## MQTT Quality of Service + +MQTT offers 3 quality of service levels. + +Each message (or topic subscription) can define a quality of service that is +associated with it. The quality of service level defined on a topic is the +maximum level a client is willing to accept. The quality of service level on a +message is the desired quality of service level for this message. The broker +will attempt to deliver messages to subscribers at the highest quality of +service level based on what is defined on the message and topic subscription. + +Each quality of service level offers a level of guarantee by which a message is +sent or received: + +- QoS 0: `AT MOST ONCE` + + Guarantees that a particular message is only ever received by the subscriber + a maximum of one time. This does mean that the message may never arrive. The + sender and the receiver will attempt to deliver the message, but if something + fails and the message does not reach it's destination (say due to a network + connection) the message may be lost. This QoS has the least network traffic + overhead and the least burden on the client and the broker and is often useful + for telemetry data where it doesn't matter if some of the data is lost. + +- QoS 1: `AT LEAST ONCE` + + Guarantees that a message will reach it's intended recipient one or more + times. The sender will continue to send the message until it receives an + acknowledgment from the recipient, confirming it has received the message. The + result of this QoS is that the recipient may receive the message multiple + times, and also increases the network overhead than QoS 0, (due to acks). In + addition more burden is placed on the sender as it needs to store the message + and retry should it fail to receive an ack in a reasonable time. + +- QoS 2: `EXACTLY ONCE` + + The most costly of the QoS (in terms of network traffic and burden on sender + and receiver) this QoS will ensure that the message is received by a recipient + exactly one time. This ensures that the receiver never gets any duplicate + copies of the message and will eventually get it, but at the extra cost of + network overhead and complexity required on the sender and receiver. + +## MQTT Retain Messages + +MQTT has an interesting feature in which messages can be "retained" for a +particular address. This means that once a retain message has been sent to an +address, any new subscribers to that address will receive the last sent retain +message before any others messages, this happens even if the retained message +was sent before a client has connected or subscribed. An example of where this +feature might be useful is in environments such as IoT where devices need to +quickly get the current state of a system when they are on boarded into a +system. + +## Will Messages + +A will message can be sent when a client initially connects to a broker. +Clients are able to set a "will message" as part of the connect packet. If the +client abnormally disconnects, say due to a device or network failure the +broker will proceed to publish the will message to the specified address (as +defined also in the connect packet). Other subscribers to the will topic will +receive the will message and can react accordingly. This feature can be useful +in an IoT style scenario to detect errors across a potentially large scale +deployment of devices. + +## Debug Logging + +Detailed protocol logging (e.g. packets in/out) can be activated via the +following steps: + +1. Open `/etc/logging.properties` + +2. Add `org.apache.activemq.artemis.core.protocol.mqtt` to the `loggers` list. + +3. Add this line to enable `TRACE` logging for this new logger: + `logger.org.apache.activemq.artemis.core.protocol.mqtt.level=TRACE` + +4. Ensure the `level` for the `handler` you want to log the message doesn't + block the `TRACE` logging. For example, modify the `level` of the `CONSOLE` + `handler` like so: `handler.CONSOLE.level=TRACE`. + +The MQTT specification doesn't dictate the format of the payloads which clients +publish. As far as the broker is concerned a payload is just just an array of +bytes. However, to facilitate logging the broker will encode the payloads as +UTF-8 strings and print them up to 256 characters. Payload logging is limited +to avoid filling the logs with potentially hundreds of megabytes of unhelpful +information. + + +## Wild card subscriptions + +MQTT addresses are hierarchical much like a file system, and they use a special +character (i.e. `/` by default) to separate hierarchical levels. Subscribers +are able to subscribe to specific topics or to whole branches of a hierarchy. + +To subscribe to branches of an address hierarchy a subscriber can use wild +cards. These wild cards (including the aforementioned separator) are +configurable. See the [Wildcard +Syntax](wildcard-syntax.md#customizing-the-syntax) chapter for details about +how to configure custom wild cards. + +There are 2 types of wild cards in MQTT: + +- **Multi level** (`#` by default) + + Adding this wild card to an address would match all branches of the address + hierarchy under a specified node. For example: `/uk/#` Would match + `/uk/cities`, `/uk/cities/newcastle` and also `/uk/rivers/tyne`. Subscribing to + an address `#` would result in subscribing to all topics in the broker. This + can be useful, but should be done so with care since it has significant + performance implications. + +- **Single level** (`+` by default) + + Matches a single level in the address hierarchy. For example `/uk/+/stores` + would match `/uk/newcastle/stores` but not `/uk/cities/newcastle/stores`. + diff --git a/docs/user-manual/en/network-isolation.md b/docs/user-manual/en/network-isolation.md index 78426e37dfd..d864d7b785a 100644 --- a/docs/user-manual/en/network-isolation.md +++ b/docs/user-manual/en/network-isolation.md @@ -1,15 +1,18 @@ # Network Isolation (Split Brain) -It is possible that if a replicated live or backup server becomes isolated in a network that failover will occur and you will end up -with 2 live servers serving messages in a cluster, this we call split brain. There are different configurations you can choose -from that will help mitigate this problem +It is possible that if a replicated live or backup server becomes isolated in a +network that failover will occur and you will end up with 2 live servers +serving messages in a cluster, this we call split brain. There are different +configurations you can choose from that will help mitigate this problem ## Quorum Voting -Quorum voting is used by both the live and the backup to decide what to do if a replication connection is disconnected. -Basically the server will request each live server in the cluster to vote as to whether it thinks the server it is replicating -to or from is still alive. You can also configure the time for which the quorum manager will wait for the quorum vote response. -The default time is 30 sec you can configure like so for master and also for the slave: +Quorum voting is used by both the live and the backup to decide what to do if a +replication connection is disconnected. Basically the server will request each +live server in the cluster to vote as to whether it thinks the server it is +replicating to or from is still alive. You can also configure the time for which +the quorum manager will wait for the quorum vote response. The default time is 30 +seconds you can configure like so for master and also for the slave: ```xml @@ -21,18 +24,23 @@ The default time is 30 sec you can configure like so for master and also for the ``` -This being the case the minimum number of live/backup pairs needed is 3. If less than 3 pairs -are used then the only option is to use a Network Pinger which is explained later in this chapter or choose how you want each server to -react which the following details: - +This being the case the minimum number of live/backup pairs needed is 3. If less +than 3 pairs are used then the only option is to use a Network Pinger which is +explained later in this chapter or choose how you want each server to react which +the following details: + ### Backup Voting -By default if a replica loses its replication connection to the live broker it makes a decision as to whether to start or not -with a quorum vote. This of course requires that there be at least 3 pairs of live/backup nodes in the cluster. For a 3 node -cluster it will start if it gets 2 votes back saying that its live server is no longer available, for 4 nodes this would be -3 votes and so on. When a backup loses connection to the master it will keep voting for a quorum until it either receives a vote -allowing it to start or it detects that the master is still live. for the latter it will then restart as a backup. How many votes -and how long between each vote the backup should wait is configured like so: +By default if a replica loses its replication connection to the live broker it +makes a decision as to whether to start or not with a quorum vote. This of +course requires that there be at least 3 pairs of live/backup nodes in the +cluster. For a 3 node cluster it will start if it gets 2 votes back saying that +its live server is no longer available, for 4 nodes this would be 3 votes and +so on. When a backup loses connection to the master it will keep voting for a +quorum until it either receives a vote allowing it to start or it detects that +the master is still live. for the latter it will then restart as a backup. How +many votes and how long between each vote the backup should wait is configured +like so: ```xml @@ -45,8 +53,9 @@ and how long between each vote the backup should wait is configured like so: ``` -It's also possible to statically set the quorum size that should be used for the case where the cluster size is known up front, -this is done on the Replica Policy like so: +It's also possible to statically set the quorum size that should be used for +the case where the cluster size is known up front, this is done on the Replica +Policy like so: ```xml @@ -58,16 +67,18 @@ this is done on the Replica Policy like so: ``` -In this example the quorum size is set to 2 so if you were using a single pair and the backup lost connectivity it would -never start. +In this example the quorum size is set to 2 so if you were using a single pair +and the backup lost connectivity it would never start. ### Live Voting -By default, if the live server loses its replication connection then it will just carry on and wait for a backup to reconnect -and start replicating again. In the event of a possible split brain scenario this may mean that the live stays live even though -the backup has been activated. It is possible to configure the live server to vote for a quorum if this happens, in this way -if the live server doesn't not receive a majority vote then it will shutdown. This is done by setting the _vote-on-replication-failure_ -to true. +By default, if the live server loses its replication connection then it will +just carry on and wait for a backup to reconnect and start replicating again. +In the event of a possible split brain scenario this may mean that the live +stays live even though the backup has been activated. It is possible to +configure the live server to vote for a quorum if this happens, in this way if +the live server doesn't not receive a majority vote then it will shutdown. This +is done by setting the _vote-on-replication-failure_ to true. ```xml @@ -79,22 +90,24 @@ to true. ``` -As in the backup policy it is also possible to statically configure the quorum size. +As in the backup policy it is also possible to statically configure the quorum +size. ## Pinging the network -You may configure one more addresses on the broker.xml that are part of your network topology, that will be pinged through the life cycle of the server. +You may configure one more addresses on the broker.xml that are part of your +network topology, that will be pinged through the life cycle of the server. The server will stop itself until the network is back on such case. -If you execute the create command passing a -ping argument, you will create a default xml that is ready to be used with network checks: +If you execute the create command passing a -ping argument, you will create a +default xml that is ready to be used with network checks: ``` ./artemis create /myDir/myServer --ping 10.0.0.1 ``` - This XML part will be added to your broker.xml: ```xml @@ -126,10 +139,8 @@ Use this to use an HTTP server to validate the network ``` - -Once you lose connectivity towards 10.0.0.1 on the given example -, you will see see this output at the server: - +Once you lose connectivity towards 10.0.0.1 on the given example, you will see +see this output at the server: ``` 09:49:24,562 WARN [org.apache.activemq.artemis.core.server.NetworkHealthCheck] Ping Address /10.0.0.1 wasn't reacheable @@ -178,8 +189,9 @@ Once you re establish your network connections towards the configured check list 09:53:23,556 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.6.0 [0.0.0.0, nodeID=04fd5dd8-b18c-11e6-9efe-6a0001921ad0] ``` -# Warning - -> Make sure you understand your network topology as this is meant to validate your network. -> Using IPs that could eventually disappear or be partially visible may defeat the purpose. -> You can use a list of multiple IPs. Any successful ping will make the server OK to continue running +> ## Warning +> +> Make sure you understand your network topology as this is meant to validate +> your network. Using IPs that could eventually disappear or be partially +> visible may defeat the purpose. You can use a list of multiple IPs. Any +> successful ping will make the server OK to continue running diff --git a/docs/user-manual/en/openwire.md b/docs/user-manual/en/openwire.md new file mode 100644 index 00000000000..31ada924ce0 --- /dev/null +++ b/docs/user-manual/en/openwire.md @@ -0,0 +1,112 @@ +# OpenWire + +Apache ActiveMQ Artemis supports the +[OpenWire](http://activemq.apache.org/openwire.html) protocol so that an Apache +ActiveMQ 5.x JMS client can talk directly to an Apache ActiveMQ Artemis server. +By default there is an `acceptor` configured to accept OpenWire connections on +port `61616`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for OpenWire. + +Refer to the OpenWire examples for a look at this functionality in action. + +## Connection Monitoring + +OpenWire has a few parameters to control how each connection is monitored, they +are: + +- `maxInactivityDuration` + + It specifies the time (milliseconds) after which the connection is closed by + the broker if no data was received. Default value is 30000. + +- `maxInactivityDurationInitalDelay` + + It specifies the maximum delay (milliseconds) before inactivity monitoring is + started on the connection. It can be useful if a broker is under load with many + connections being created concurrently. Default value is 10000. + +- `useInactivityMonitor` + + A value of false disables the InactivityMonitor completely and connections + will never time out. By default it is enabled. On broker side you don't neet + set this. Instead you can set the connection-ttl to -1. + +- `useKeepAlive` + + Whether or not to send a KeepAliveInfo on an idle connection to prevent it + from timing out. Enabled by default. Disabling the keep alive will still make + connections time out if no data was received on the connection for the + specified amount of time. + +Note at the beginning the InactivityMonitor negotiates the appropriate +`maxInactivityDuration` and `maxInactivityDurationInitalDelay`. The shortest +duration is taken for the connection. + +Fore more details please see [ActiveMQ +InactivityMonitor](http://activemq.apache.org/activemq-inactivitymonitor.html). + +## Disable/Enable Advisories + +By default, advisory topics ([ActiveMQ +Advisory](http://activemq.apache.org/advisory-message.html)) are created in +order to send certain type of advisory messages to listening clients. As a +result, advisory addresses and queues will be displayed on the management +console, along with user deployed addresses and queues. This sometimes cause +confusion because the advisory objects are internally managed without user +being aware of them. In addition, users may not want the advisory topics at all +(they cause extra resources and performance penalty) and it is convenient to +disable them at all from the broker side. + +The protocol provides two parameters to control advisory behaviors on the +broker side. + +- `supportAdvisory` + + Whether or not the broker supports advisory messages. If the value is true, + advisory addresses/queues will be created. If the value is false, no advisory + addresses/queues are created. Default value is `true`. + +- `suppressInternalManagementObjects` + + Whether or not the advisory addresses/queues, if any, will be registered to + management service (e.g. JMX registry). If set to true, no advisory + addresses/queues will be registered. If set to false, those are registered and + will be displayed on the management console. Default value is `true`. + +The two parameters are configured on an OpenWire `acceptor`, e.g.: + +```xml +tcp://localhost:61616?protocols=OPENWIRE;supportAdvisory=true;suppressInternalManagementObjects=false +``` + +## Virtual Topic Consumer Destination Translation + +For existing OpenWire consumers of virtual topic destinations it is possible to +configure a mapping function that will translate the virtual topic consumer +destination into a FQQN address. This address then represents the consumer as a +multicast binding to an address representing the virtual topic. + +The configuration string property `virtualTopicConsumerWildcards` has two parts +seperated by a `;`. The first is the 5.x style destination filter that +identifies the destination as belonging to a virtual topic. The second +identifies the number of `paths` that identify the consumer queue such that it +can be parsed from the destination. For example, the default 5.x virtual topic +with consumer prefix of `Consumer.*.`, would require a +`virtualTopicConsumerWildcards` filter of `Consumer.*.>;2`. As a url parameter +this transforms to `Consumer.*.%3E%3B2` when the url significant characters +`>;` are escaped with their hex code points. In an `acceptor` url it would be: + +```xml +tcp://localhost:61616?protocols=OPENWIRE;virtualTopicConsumerWildcards=Consumer.*.%3E%3B2 +``` + +This will translate `Consumer.A.VirtualTopic.Orders` into a FQQN of +`VirtualTopic.Orders::Consumer.A` using the int component `2` of the +configuration to identify the consumer queue as the first two paths of the +destination. `virtualTopicConsumerWildcards` is multi valued using a `,` +separator. + +Please see Virtual Topic Mapping example contained in the OpenWire +[examples](examples.md). \ No newline at end of file diff --git a/docs/user-manual/en/paging.md b/docs/user-manual/en/paging.md index a6ce648bb2f..60e58d0cf3b 100644 --- a/docs/user-manual/en/paging.md +++ b/docs/user-manual/en/paging.md @@ -1,77 +1,70 @@ # Paging -Apache ActiveMQ Artemis transparently supports huge queues containing millions of -messages while the server is running with limited memory. +Apache ActiveMQ Artemis transparently supports huge queues containing millions +of messages while the server is running with limited memory. -In such a situation it's not possible to store all of the queues in -memory at any one time, so Apache ActiveMQ Artemis transparently *pages* messages into -and out of memory as they are needed, thus allowing massive queues with -a low memory footprint. +In such a situation it's not possible to store all of the queues in memory at +any one time, so Apache ActiveMQ Artemis transparently *pages* messages into +and out of memory as they are needed, thus allowing massive queues with a low +memory footprint. -Apache ActiveMQ Artemis will start paging messages to disk, when the size of all -messages in memory for an address exceeds a configured maximum size. +Apache ActiveMQ Artemis will start paging messages to disk, when the size of +all messages in memory for an address exceeds a configured maximum size. The default configuration from Artemis has destinations with paging. ## Page Files Messages are stored per address on the file system. Each address has an -individual folder where messages are stored in multiple files (page -files). Each file will contain messages up to a max configured size -(`page-size-bytes`). The system will navigate on the files as needed, -and it will remove the page file as soon as all the messages are -acknowledged up to that point. +individual folder where messages are stored in multiple files (page files). +Each file will contain messages up to a max configured size +(`page-size-bytes`). The system will navigate the files as needed, and it +will remove the page file as soon as all the messages are acknowledged up to +that point. Browsers will read through the page-cursor system. -Consumers with selectors will also navigate through the page-files and it will ignore messages that don't match the criteria. +Consumers with selectors will also navigate through the page-files and it will +ignore messages that don't match the criteria. + > *Warning:* -> When you have a queue, and consumers filtering the queue with a very restrictive selector you may get into a situation where you won't be able to read more data from paging until you consume messages from the queue. > -> Example: in one consumer you make a selector as 'color="red"' -> but you only have one color red 1 millions messages after blue, you won't be able to consume red until you consume blue ones. +> When you have a queue, and consumers filtering the queue with a very +> restrictive selector you may get into a situation where you won't be able to +> read more data from paging until you consume messages from the queue. +> +> Example: in one consumer you make a selector as 'color="red"' but you only +> have one color red 1 millions messages after blue, you won't be able to +> consume red until you consume blue ones. > -> This is different to browsing as we will "browse" the entire queue looking for messages and while we "depage" messages while feeding the queue. +> This is different to browsing as we will "browse" the entire queue looking +> for messages and while we "depage" messages while feeding the queue. ### Configuration -You can configure the location of the paging folder - -Global paging parameters are specified on the main configuration file -(`broker.xml`). - - - ... - /somewhere/paging-directory - ... +You can configure the location of the paging folder in `broker.xml`. - Property Name Description Default - -------------------- --------------------------------------------------------------------------------------------------------------------------- ------------- - `paging-directory` Where page files are stored. Apache ActiveMQ Artemis will create one folder for each address being paged under this configured location. data/paging - - : Paging Configuration Parameters +- `paging-directory` Where page files are stored. Apache ActiveMQ Artemis will + create one folder for each address being paged under this configured + location. Default is `data/paging`. ## Paging Mode As soon as messages delivered to an address exceed the configured size, that address alone goes into page mode. -> **Note** +> **Note:** > -> Paging is done individually per address. If you configure a -> max-size-bytes for an address, that means each matching address will -> have a maximum size that you specified. It DOES NOT mean that the -> total overall size of all matching addresses is limited to -> max-size-bytes. +> Paging is done individually per address. If you configure a max-size-bytes +> for an address, that means each matching address will have a maximum size +> that you specified. It DOES NOT mean that the total overall size of all +> matching addresses is limited to max-size-bytes. ### Configuration -Configuration is done at the address settings, done at the main -configuration file (`broker.xml`). +Configuration is done at the address settings in `broker.xml`. ```xml @@ -85,117 +78,90 @@ configuration file (`broker.xml`). This is the list of available parameters on the address settings. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Property NameDescriptionDefault
`max-size-bytes`What's the max memory the address could have before entering on page mode.-1 (disabled)
`page-size-bytes`The size of each page file used on the paging system10MiB (10 \* 1024 \* 1024 bytes)
`address-full-policy`This must be set to PAGE for paging to enable. If the value is PAGE then further messages will be paged to disk. If the value is DROP then further messages will be silently dropped. If the value is FAIL then the messages will be dropped and the client message producers will receive an exception. If the value is BLOCK then client message producers will block when they try and send further messages.PAGE
`page-max-cache-size`The system will keep up to `page-max-cache-size` page files in memory to optimize IO during paging navigation.5
+Property Name|Description|Default +---|---|--- +`max-size-bytes`|What's the max memory the address could have before entering on page mode.|-1 (disabled) +`page-size-bytes`|The size of each page file used on the paging system|10MB +`address-full-policy`|This must be set to `PAGE` for paging to enable. If the value is `PAGE` then further messages will be paged to disk. If the value is `DROP` then further messages will be silently dropped. If the value is `FAIL` then the messages will be dropped and the client message producers will receive an exception. If the value is `BLOCK` then client message producers will block when they try and send further messages.|`PAGE` +`page-max-cache-size`|The system will keep up to `page-max-cache-size` page files in memory to optimize IO during paging navigation.|5 ## Global Max Size -Beyond the max-size-bytes on the address you can also set the global-max-size on the main configuration. If you set max-size-bytes = -1 on paging the global-max-size can still be used. +Beyond the `max-size-bytes` on the address you can also set the global-max-size +on the main configuration. If you set `max-size-bytes` = `-1` on paging the +`global-max-size` can still be used. -When you have more messages than what is configured global-max-size any new produced message will make that destination to go through its paging policy. +When you have more messages than what is configured `global-max-size` any new +produced message will make that destination to go through its paging policy. -global-max-size is calculated as half of the max memory available to the Java Virtual Machine, unless specified on the broker.xml configuration. +`global-max-size` is calculated as half of the max memory available to the Java +Virtual Machine, unless specified on the `broker.xml` configuration. ## Dropping messages -Instead of paging messages when the max size is reached, an address can -also be configured to just drop messages when the address is full. +Instead of paging messages when the max size is reached, an address can also be +configured to just drop messages when the address is full. -To do this just set the `address-full-policy` to `DROP` in the address -settings +To do this just set the `address-full-policy` to `DROP` in the address settings ## Dropping messages and throwing an exception to producers -Instead of paging messages when the max size is reached, an address can -also be configured to drop messages and also throw an exception on the -client-side when the address is full. +Instead of paging messages when the max size is reached, an address can also be +configured to drop messages and also throw an exception on the client-side when +the address is full. -To do this just set the `address-full-policy` to `FAIL` in the address -settings +To do this just set the `address-full-policy` to `FAIL` in the address settings ## Blocking producers -Instead of paging messages when the max size is reached, an address can -also be configured to block producers from sending further messages when -the address is full, thus preventing the memory being exhausted on the -server. +Instead of paging messages when the max size is reached, an address can also be +configured to block producers from sending further messages when the address is +full, thus preventing the memory being exhausted on the server. -When memory is freed up on the server, producers will automatically -unblock and be able to continue sending. +When memory is freed up on the server, producers will automatically unblock and +be able to continue sending. To do this just set the `address-full-policy` to `BLOCK` in the address settings -In the default configuration, all addresses are configured to block -producers after 10 MiB of data are in the address. +In the default configuration, all addresses are configured to block producers +after 10 MiB of data are in the address. ## Caution with Addresses with Multiple Multicast Queues -When a message is routed to an address that has multiple multicast queues bound to -it, e.g. a JMS subscription in a Topic, there is only 1 copy of the -message in memory. Each queue only deals with a reference to this. -Because of this the memory is only freed up once all queues referencing -the message have delivered it. +When a message is routed to an address that has multiple multicast queues bound +to it, e.g. a JMS subscription in a Topic, there is only 1 copy of the message +in memory. Each queue only deals with a reference to this. Because of this the +memory is only freed up once all queues referencing the message have delivered +it. -If you have a single lazy subscription, the entire address will suffer -IO performance hit as all the queues will have messages being sent -through an extra storage on the paging system. +If you have a single lazy subscription, the entire address will suffer IO +performance hit as all the queues will have messages being sent through an +extra storage on the paging system. For example: -- An address has 10 multicast queues +- An address has 10 multicast queues -- One of the queues does not deliver its messages (maybe because of a - slow consumer). +- One of the queues does not deliver its messages (maybe because of a + slow consumer). -- Messages continually arrive at the address and paging is started. +- Messages continually arrive at the address and paging is started. -- The other 9 queues are empty even though messages have been sent. +- The other 9 queues are empty even though messages have been sent. -In this example all the other 9 queues will be consuming messages from -the page system. This may cause performance issues if this is an -undesirable state. +In this example all the other 9 queues will be consuming messages from the page +system. This may cause performance issues if this is an undesirable state. ## Max Disk Usage -The System will perform scans on the disk to determine if the disk is beyond a configured limit. -These are configured through 'max-disk-usage' in percentage. Once that limit is reached any -message will be blocked. (unless the protocol doesn't support flow control on which case there will be an exception thrown and the connection for those clients dropped). +The System will perform scans on the disk to determine if the disk is beyond a +configured limit. These are configured through `max-disk-usage` in percentage. +Once that limit is reached any message will be blocked. (unless the protocol +doesn't support flow control on which case there will be an exception thrown +and the connection for those clients dropped). ## Example -See the [examples](examples.md) chapter for an example which shows how to use paging with Apache ActiveMQ Artemis. +See the [Paging Example](examples.md#paging) which shows how to use paging with +Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/perf-tuning.md b/docs/user-manual/en/perf-tuning.md index 81f1f445160..3b2d37a64ad 100644 --- a/docs/user-manual/en/perf-tuning.md +++ b/docs/user-manual/en/perf-tuning.md @@ -5,269 +5,251 @@ performance. ## Tuning persistence -- To get the best performance from Apache ActiveMQ Artemis whilst - using persistent messages it is recommended that the file store - is used. Apache ActiveMQ Artemis also supports JDBC persistence, - but there is a performance cost when persisting to a database vs - local disk. -- Put the message journal on its own physical volume. If the disk is - shared with other processes e.g. transaction co-ordinator, database - or other journals which are also reading and writing from it, then - this may greatly reduce performance since the disk head may be - skipping all over the place between the different files. One of the - advantages of an append only journal is that disk head movement is - minimised - this advantage is destroyed if the disk is shared. If - you're using paging or large messages make sure they're ideally put - on separate volumes too. - -- Minimum number of journal files. Set `journal-min-files` to a number - of files that would fit your average sustainable rate. This number - represents the lower threshold of the journal file pool. - -- To set the upper threshold of the journal file pool. (`journal-min-files` being - the lower threshold). Set `journal-pool-files` to a number that represents - something near your maximum expected load. The journal will spill over - the pool should it need to, but will shrink back to the upper threshold, - when possible. This allows reuse of files, without taking up more disk - space than required. If you see new files being created on the journal - data directory too often, i.e. lots of data is being persisted, - you need to increase the journal-pool-size, this way the journal would - reuse more files instead of creating new data files, increasing performance - -- Journal file size. The journal file size should be aligned to the - capacity of a cylinder on the disk. The default value 10MiB should - be enough on most systems. - -- Use AIO journal. If using Linux, try to keep your journal type as - AIO. AIO will scale better than Java NIO. - -- Tune `journal-buffer-timeout`. The timeout can be increased to - increase throughput at the expense of latency. - -- If you're running AIO you might be able to get some better - performance by increasing `journal-max-io`. DO NOT change this - parameter if you are running NIO. - -- If you are 100% sure you don't need power failure durability guarantees, - disable `journal-data-sync` and use `NIO` or `MAPPED` journal: - you'll benefit a huge performance boost on writes - with process failure durability guarantees. +- To get the best performance from Apache ActiveMQ Artemis whilst using + persistent messages it is recommended that the file store is used. Apache + ActiveMQ Artemis also supports JDBC persistence, but there is a performance + cost when persisting to a database vs local disk. + +- Put the message journal on its own physical volume. If the disk is shared + with other processes e.g. transaction co-ordinator, database or other + journals which are also reading and writing from it, then this may greatly + reduce performance since the disk head may be skipping all over the place + between the different files. One of the advantages of an append only journal is + that disk head movement is minimised - this advantage is destroyed if the disk + is shared. If you're using paging or large messages make sure they're ideally + put on separate volumes too. + +- Minimum number of journal files. Set `journal-min-files` to a number of files + that would fit your average sustainable rate. This number represents the + lower threshold of the journal file pool. + +- To set the upper threshold of the journal file pool. (`journal-min-files` being + the lower threshold). Set `journal-pool-files` to a number that represents + something near your maximum expected load. The journal will spill over the + pool should it need to, but will shrink back to the upper threshold, when + possible. This allows reuse of files, without taking up more disk space than + required. If you see new files being created on the journal data directory too + often, i.e. lots of data is being persisted, you need to increase the + journal-pool-size, this way the journal would reuse more files instead of + creating new data files, increasing performance + +- Journal file size. The journal file size should be aligned to the capacity of + a cylinder on the disk. The default value 10MiB should be enough on most + systems. + +- Use `ASYNCIO` journal. If using Linux, try to keep your journal type as + `ASYNCIO`. `ASYNCIO` will scale better than Java NIO. + +- Tune `journal-buffer-timeout`. The timeout can be increased to increase + throughput at the expense of latency. + +- If you're running `ASYNCIO` you might be able to get some better performance by + increasing `journal-max-io`. DO NOT change this parameter if you are running + NIO. + +- If you are 100% sure you don't need power failure durability guarantees, + disable `journal-data-sync` and use `NIO` or `MAPPED` journal: you'll benefit + a huge performance boost on writes with process failure durability guarantees. ## Tuning JMS -There are a few areas where some tweaks can be done if you are using the -JMS API - -- Disable message id. Use the `setDisableMessageID()` method on the - `MessageProducer` class to disable message ids if you don't need - them. This decreases the size of the message and also avoids the - overhead of creating a unique ID. - -- Disable message timestamp. Use the `setDisableMessageTimeStamp()` - method on the `MessageProducer` class to disable message timestamps - if you don't need them. - -- Avoid `ObjectMessage`. `ObjectMessage` is convenient but it comes at - a cost. The body of a `ObjectMessage` uses Java serialization to - serialize it to bytes. The Java serialized form of even small - objects is very verbose so takes up a lot of space on the wire, also - Java serialization is slow compared to custom marshalling - techniques. Only use `ObjectMessage` if you really can't use one of - the other message types, i.e. if you really don't know the type of - the payload until run-time. - -- Avoid `AUTO_ACKNOWLEDGE`. `AUTO_ACKNOWLEDGE` mode requires an - acknowledgement to be sent from the server for each message received - on the client, this means more traffic on the network. If you can, - use `DUPS_OK_ACKNOWLEDGE` or use `CLIENT_ACKNOWLEDGE` or a - transacted session and batch up many acknowledgements with one - acknowledge/commit. - -- Avoid durable messages. By default JMS messages are durable. If you - don't really need durable messages then set them to be non-durable. - Durable messages incur a lot more overhead in persisting them to - storage. - -- Batch many sends or acknowledgements in a single transaction. - Apache ActiveMQ Artemis will only require a network round trip on the commit, not - on every send or acknowledgement. +There are a few areas where some tweaks can be done if you are using the JMS +API + +- Disable message id. Use the `setDisableMessageID()` method on the + `MessageProducer` class to disable message ids if you don't need them. This + decreases the size of the message and also avoids the overhead of creating a + unique ID. + +- Disable message timestamp. Use the `setDisableMessageTimeStamp()` method on + the `MessageProducer` class to disable message timestamps if you don't need + them. + +- Avoid `ObjectMessage`. `ObjectMessage` is convenient but it comes at a cost. + The body of a `ObjectMessage` uses Java serialization to serialize it to + bytes. The Java serialized form of even small objects is very verbose so takes + up a lot of space on the wire, also Java serialization is slow compared to + custom marshalling techniques. Only use `ObjectMessage` if you really can't use + one of the other message types, i.e. if you really don't know the type of the + payload until run-time. + +- Avoid `AUTO_ACKNOWLEDGE`. `AUTO_ACKNOWLEDGE` mode requires an acknowledgement + to be sent from the server for each message received on the client, this + means more traffic on the network. If you can, use `DUPS_OK_ACKNOWLEDGE` or use + `CLIENT_ACKNOWLEDGE` or a transacted session and batch up many acknowledgements + with one acknowledge/commit. + +- Avoid durable messages. By default JMS messages are durable. If you don't + really need durable messages then set them to be non-durable. Durable + messages incur a lot more overhead in persisting them to storage. + +- Batch many sends or acknowledgements in a single transaction. Apache + ActiveMQ Artemis will only require a network round trip on the commit, not on + every send or acknowledgement. ## Other Tunings -There are various other places in Apache ActiveMQ Artemis where we can perform some -tuning: - -- Use Asynchronous Send Acknowledgements. If you need to send durable - messages non transactionally and you need a guarantee that they have - reached the server by the time the call to send() returns, don't set - durable messages to be sent blocking, instead use asynchronous send - acknowledgements to get your acknowledgements of send back in a - separate stream, see [Guarantees of sends and commits](send-guarantees.md) - for more information on this. - -- Use pre-acknowledge mode. With pre-acknowledge mode, messages are - acknowledged `before` they are sent to the client. This reduces the - amount of acknowledgement traffic on the wire. For more information - on this, see [Extra Acknowledge Modes](pre-acknowledge.md). - -- Disable security. You may get a small performance boost by disabling - security by setting the `security-enabled` parameter to `false` in - `broker.xml`. - -- Disable persistence. If you don't need message persistence, turn it - off altogether by setting `persistence-enabled` to false in - `broker.xml`. - -- Sync transactions lazily. Setting `journal-sync-transactional` to - `false` in `broker.xml` can give you better - transactional persistent performance at the expense of some - possibility of loss of transactions on failure. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- Sync non transactional lazily. Setting - `journal-sync-non-transactional` to `false` in - `broker.xml` can give you better non-transactional - persistent performance at the expense of some possibility of loss of - durable messages on failure. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- Send messages non blocking. Setting `block-on-durable-send` and - `block-on-non-durable-send` to `false` in the jms config (if - you're using JMS and JNDI) or directly on the ServerLocator. This - means you don't have to wait a whole network round trip for every - message sent. See [Guarantees of sends and commits](send-guarantees.md) - for more information. - -- If you have very fast consumers, you can increase - consumer-window-size. This effectively disables consumer flow - control. - -- Use the core API not JMS. Using the JMS API you will have slightly - lower performance than using the core API, since all JMS operations - need to be translated into core operations before the server can - handle them. If using the core API try to use methods that take - `SimpleString` as much as possible. `SimpleString`, unlike - java.lang.String does not require copying before it is written to - the wire, so if you re-use `SimpleString` instances between calls - then you can avoid some unnecessary copying. - -- If using frameworks like Spring, configure destinations permanently broker side - and enable `destinationCache` on the client side. - See the [Setting The Destination Cache](using-jms.md) - for more information on this. +There are various other places in Apache ActiveMQ Artemis where we can perform +some tuning: + +- Use Asynchronous Send Acknowledgements. If you need to send durable messages + non transactionally and you need a guarantee that they have reached the + server by the time the call to send() returns, don't set durable messages to be + sent blocking, instead use asynchronous send acknowledgements to get your + acknowledgements of send back in a separate stream, see [Guarantees of sends + and commits](send-guarantees.md) for more information on this. + +- Use pre-acknowledge mode. With pre-acknowledge mode, messages are + acknowledged `before` they are sent to the client. This reduces the amount of + acknowledgement traffic on the wire. For more information on this, see [Extra + Acknowledge Modes](pre-acknowledge.md). + +- Disable security. You may get a small performance boost by disabling security + by setting the `security-enabled` parameter to `false` in `broker.xml`. + +- Disable persistence. If you don't need message persistence, turn it off + altogether by setting `persistence-enabled` to false in `broker.xml`. + +- Sync transactions lazily. Setting `journal-sync-transactional` to `false` in + `broker.xml` can give you better transactional persistent performance at the + expense of some possibility of loss of transactions on failure. See + [Guarantees of sends and commits](send-guarantees.md) for more information. + +- Sync non transactional lazily. Setting `journal-sync-non-transactional` to + `false` in `broker.xml` can give you better non-transactional persistent + performance at the expense of some possibility of loss of durable messages on + failure. See [Guarantees of sends and commits](send-guarantees.md) for more + information. + +- Send messages non blocking. Setting `block-on-durable-send` and + `block-on-non-durable-send` to `false` in the jms config (if you're using JMS + and JNDI) or directly on the ServerLocator. This means you don't have to wait a + whole network round trip for every message sent. See [Guarantees of sends and + commits](send-guarantees.md) for more information. + +- If you have very fast consumers, you can increase consumer-window-size. This + effectively disables consumer flow control. + +- Use the core API not JMS. Using the JMS API you will have slightly lower + performance than using the core API, since all JMS operations need to be + translated into core operations before the server can handle them. If using the + core API try to use methods that take `SimpleString` as much as possible. + `SimpleString`, unlike java.lang.String does not require copying before it is + written to the wire, so if you re-use `SimpleString` instances between calls + then you can avoid some unnecessary copying. + +- If using frameworks like Spring, configure destinations permanently broker + side and enable `destinationCache` on the client side. See the [Setting The + Destination Cache](using-jms.md) for more information on this. ## Tuning Transport Settings -- TCP buffer sizes. If you have a fast network and fast machines you - may get a performance boost by increasing the TCP send and receive - buffer sizes. See the [Configuring the Transport](configuring-transports.md) - for more information on this. - - > **Note** - > - > Note that some operating systems like later versions of Linux - > include TCP auto-tuning and setting TCP buffer sizes manually can - > prevent auto-tune from working and actually give you worse - > performance! - -- Increase limit on file handles on the server. If you expect a lot of - concurrent connections on your servers, or if clients are rapidly - opening and closing connections, you should make sure the user - running the server has permission to create sufficient file handles. - - This varies from operating system to operating system. On Linux - systems you can increase the number of allowable open file handles - in the file `/etc/security/limits.conf` e.g. add the lines - - serveruser soft nofile 20000 - serveruser hard nofile 20000 - - This would allow up to 20000 file handles to be open by the user - `serveruser`. - -- Use `batch-delay` and set `direct-deliver` to false for the best - throughput for very small messages. Apache ActiveMQ Artemis comes with a - preconfigured connector/acceptor pair (`netty-throughput`) in - `broker.xml` and JMS connection factory - (`ThroughputConnectionFactory`) in `activemq-jms.xml`which can be - used to give the very best throughput, especially for small - messages. See the [Configuring the Transport](configuring-transports.md) - for more information on this. +- TCP buffer sizes. If you have a fast network and fast machines you may get a + performance boost by increasing the TCP send and receive buffer sizes. See + the [Configuring the Transport](configuring-transports.md) for more information + on this. + + > **Note:** + > + > Note that some operating systems like later versions of Linux include TCP + > auto-tuning and setting TCP buffer sizes manually can prevent auto-tune + > from working and actually give you worse performance! + +- Increase limit on file handles on the server. If you expect a lot of + concurrent connections on your servers, or if clients are rapidly opening and + closing connections, you should make sure the user running the server has + permission to create sufficient file handles. + + This varies from operating system to operating system. On Linux systems you + can increase the number of allowable open file handles in the file + `/etc/security/limits.conf` e.g. add the lines + + ``` + serveruser soft nofile 20000 + serveruser hard nofile 20000 + ``` + + This would allow up to 20000 file handles to be open by the user + `serveruser`. + +- Use `batch-delay` and set `direct-deliver` to false for the best throughput + for very small messages. Apache ActiveMQ Artemis comes with a preconfigured + connector/acceptor pair (`netty-throughput`) in `broker.xml` and JMS connection + factory (`ThroughputConnectionFactory`) in `activemq-jms.xml`which can be used + to give the very best throughput, especially for small messages. See the + [Configuring the Transport](configuring-transports.md) for more information on + this. ## Tuning the VM -We highly recommend you use the latest Java JVM for the best -performance. We test internally using the Sun JVM, so some of these -tunings won't apply to JDKs from other providers (e.g. IBM or JRockit) - -- Garbage collection. For smooth server operation we recommend using a - parallel garbage collection algorithm, e.g. using the JVM argument - `-XX:+UseParallelOldGC` on Sun JDKs. - -- Memory settings. Give as much memory as you can to the server. - Apache ActiveMQ Artemis can run in low memory by using paging (described in [Paging](paging.md)) but - if it can run with all queues in RAM this will improve performance. - The amount of memory you require will depend on the size and number - of your queues and the size and number of your messages. Use the JVM - arguments `-Xms` and `-Xmx` to set server available RAM. We - recommend setting them to the same high value. - - When under periods of high load, it is likely that Artemis will be generating - and destroying lots of objects. This can result in a build up of stale objects. - To reduce the chance of running out of memory and causing a full GC - (which may introduce pauses and unintentional behaviour), it is recommended that the - max heap size (`-Xmx`) for the JVM is set at least to 5 x the `global-max-size` of the broker. - As an example, in a situation where the broker is under high load and running - with a `global-max-size` of 1GB, it is recommended the the max heap size is set to 5GB. - -- Aggressive options. Different JVMs provide different sets of JVM - tuning parameters, for the Sun Hotspot JVM the full list of options - is available - [here](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html). - We recommend at least using `-XX:+AggressiveOpts`. - You may get some mileage with the other tuning parameters depending - on your OS platform and application usage patterns. +We highly recommend you use the latest Java JVM for the best performance. We +test internally using the Sun JVM, so some of these tunings won't apply to JDKs +from other providers (e.g. IBM or JRockit) + +- Garbage collection. For smooth server operation we recommend using a parallel + garbage collection algorithm, e.g. using the JVM argument + `-XX:+UseParallelOldGC` on Sun JDKs. + +- Memory settings. Give as much memory as you can to the server. Apache + ActiveMQ Artemis can run in low memory by using paging (described in + [Paging](paging.md)) but if it can run with all queues in RAM this will improve + performance. The amount of memory you require will depend on the size and + number of your queues and the size and number of your messages. Use the JVM + arguments `-Xms` and `-Xmx` to set server available RAM. We recommend setting + them to the same high value. + + When under periods of high load, it is likely that Artemis will be generating + and destroying lots of objects. This can result in a build up of stale objects. + To reduce the chance of running out of memory and causing a full GC (which may + introduce pauses and unintentional behaviour), it is recommended that the max + heap size (`-Xmx`) for the JVM is set at least to 5 x the `global-max-size` of + the broker. As an example, in a situation where the broker is under high load + and running with a `global-max-size` of 1GB, it is recommended the the max heap + size is set to 5GB. + +- Aggressive options. Different JVMs provide different sets of JVM tuning + parameters, for the Sun Hotspot JVM the full list of options is available + [here](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html). + We recommend at least using `-XX:+AggressiveOpts`. You may get some mileage + with the other tuning parameters depending on your OS platform and application + usage patterns. ## Avoiding Anti-Patterns -- Re-use connections / sessions / consumers / producers. Probably the - most common messaging anti-pattern we see is users who create a new - connection/session/producer for every message they send or every - message they consume. This is a poor use of resources. These objects - take time to create and may involve several network round trips. - Always re-use them. - - > **Note** - > - > Some popular libraries such as the Spring JMS Template are known - > to use these anti-patterns. If you're using Spring JMS Template - > and you're getting poor performance you know why. Don't blame - > Apache ActiveMQ Artemis! The Spring JMS Template can only safely be used in an - > app server which caches JMS sessions (e.g. using JCA), and only - > then for sending messages. It cannot be safely be used for - > synchronously consuming messages, even in an app server. - -- Avoid fat messages. Verbose formats such as XML take up a lot of - space on the wire and performance will suffer as result. Avoid XML - in message bodies if you can. - -- Don't create temporary queues for each request. This common - anti-pattern involves the temporary queue request-response pattern. - With the temporary queue request-response pattern a message is sent - to a target and a reply-to header is set with the address of a local - temporary queue. When the recipient receives the message they - process it then send back a response to the address specified in the - reply-to. A common mistake made with this pattern is to create a new - temporary queue on each message sent. This will drastically reduce - performance. Instead the temporary queue should be re-used for many - requests. - -- Don't use Message-Driven Beans for the sake of it. As soon as you - start using MDBs you are greatly increasing the codepath for each - message received compared to a straightforward message consumer, - since a lot of extra application server code is executed. Ask - yourself do you really need MDBs? Can you accomplish the same task - using just a normal message consumer? +- Re-use connections / sessions / consumers / producers. Probably the most + common messaging anti-pattern we see is users who create a new + connection/session/producer for every message they send or every message they + consume. This is a poor use of resources. These objects take time to create and + may involve several network round trips. Always re-use them. + + > **Note:** + > + > Some popular libraries such as the Spring JMS Template are known to use + > these anti-patterns. If you're using Spring JMS Template and you're getting + > poor performance you know why. Don't blame Apache ActiveMQ Artemis! The + > Spring JMS Template can only safely be used in an app server which caches + > JMS sessions (e.g. using JCA), and only then for sending messages. It + > cannot be safely be used for synchronously consuming messages, even in an + > app server. + +- Avoid fat messages. Verbose formats such as XML take up a lot of space on the + wire and performance will suffer as result. Avoid XML in message bodies if + you can. + +- Don't create temporary queues for each request. This common anti-pattern + involves the temporary queue request-response pattern. With the temporary + queue request-response pattern a message is sent to a target and a reply-to + header is set with the address of a local temporary queue. When the recipient + receives the message they process it then send back a response to the address + specified in the reply-to. A common mistake made with this pattern is to create + a new temporary queue on each message sent. This will drastically reduce + performance. Instead the temporary queue should be re-used for many requests. + +- Don't use Message-Driven Beans for the sake of it. As soon as you start using + MDBs you are greatly increasing the codepath for each message received + compared to a straightforward message consumer, since a lot of extra + application server code is executed. Ask yourself do you really need MDBs? Can + you accomplish the same task using just a normal message consumer? ## Troubleshooting @@ -275,17 +257,30 @@ tunings won't apply to JDKs from other providers (e.g. IBM or JRockit) In certain situations UDP used on discovery may not work. Typical situations are: -1. The nodes are behind a firewall. If your nodes are on different machines then it is possible that the firewall is blocking the multicasts. you can test this by disabling the firewall for each node or adding the appropriate rules. -2. You are using a home network or are behind a gateway. Typically home networks will redirect any UDP traffic to the Internet Service Provider which is then either dropped by the ISP or just lost. To fix this you will need to add a route to the firewall/gateway that will redirect any multicast traffic back on to the local network instead. -3. All the nodes are in one machine. If this is the case then it is a similar problem to point 2 and the same solution should fix it. Alternatively you could add a multicast route to the loopback interface. On linux the command would be: -```sh -# you should run this as root -route add -net 224.0.0.0 netmask 240.0.0.0 dev lo -``` - This will redirect any traffic directed to the 224.0.0.0 to the loopback interface. This will also work if you have no network at all. - - * on Mac OS X, the command is slightly different: -```sh -sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 -``` +1. The nodes are behind a firewall. If your nodes are on different machines + then it is possible that the firewall is blocking the multicasts. you can + test this by disabling the firewall for each node or adding the appropriate + rules. +2. You are using a home network or are behind a gateway. Typically home + networks will redirect any UDP traffic to the Internet Service Provider + which is then either dropped by the ISP or just lost. To fix this you will need + to add a route to the firewall/gateway that will redirect any multicast traffic + back on to the local network instead. +3. All the nodes are in one machine. If this is the case then it is a similar + problem to point 2 and the same solution should fix it. Alternatively you + could add a multicast route to the loopback interface. On linux the command + would be: + + ```sh + # you should run this as root + route add -net 224.0.0.0 netmask 240.0.0.0 dev lo + ``` + + This will redirect any traffic directed to the 224.0.0.0 to the loopback + interface. This will also work if you have no network at all. On Mac OS X, the + command is slightly different: + + ```sh + sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0 + ``` diff --git a/docs/user-manual/en/persistence.md b/docs/user-manual/en/persistence.md index 00159e437ed..b0b253bd5f4 100644 --- a/docs/user-manual/en/persistence.md +++ b/docs/user-manual/en/persistence.md @@ -59,7 +59,7 @@ completion when AIO informs us that the data has been persisted. Using AIO will typically provide even better performance than using Java NIO. -The AIO journal is only available when running Linux kernel 2.6 or +This journal option is only available when running Linux kernel 2.6 or later and after having installed libaio (if it's not already installed). For instructions on how to install libaio please see Installing AIO section. @@ -69,53 +69,53 @@ systems: ext2, ext3, ext4, jfs, xfs and NFSV4. For more information on libaio please see [lib AIO](libaio.md). libaio is part of the kernel project. - + ### [Memory mapped](https://en.wikipedia.org/wiki/Memory-mapped_file) The third implementation uses a file-backed [READ_WRITE](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.MapMode.html#READ_WRITE) memory mapping against the OS page cache to interface with the file system. - + This provides extremely good performance (especially under strictly process failure durability requirements), almost zero copy (actually *is* the kernel page cache) and zero garbage (from the Java HEAP perspective) operations and runs on any platform where there's a Java 4+ runtime. - + Under power failure durability requirements it will perform at least on par with the NIO journal with the only exception of Linux OS with kernel less or equals 2.6, in which the [*msync*](https://docs.oracle.com/javase/8/docs/api/java/nio/MappedByteBuffer.html#force%28%29)) implementation necessary to ensure durable writes was different (and slower) from the [*fsync*](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html#force%28boolean%29) used is case of NIO journal. - + It benefits by the configuration of OS [huge pages](https://en.wikipedia.org/wiki/Page_%28computer_memory%29), -in particular when is used a big number of journal files and sizing them as multiple of the OS page size in bytes. +in particular when is used a big number of journal files and sizing them as multiple of the OS page size in bytes. ### Standard Files The standard Apache ActiveMQ Artemis core server uses two instances of the journal: -- Bindings journal. +- Bindings journal. - This journal is used to store bindings related data. That includes - the set of queues that are deployed on the server and their - attributes. It also stores data such as id sequence counters. + This journal is used to store bindings related data. That includes + the set of queues that are deployed on the server and their + attributes. It also stores data such as id sequence counters. - The bindings journal is always a NIO journal as it is typically low - throughput compared to the message journal. + The bindings journal is always a NIO journal as it is typically low + throughput compared to the message journal. - The files on this journal are prefixed as `activemq-bindings`. Each - file has a `bindings` extension. File size is `1048576`, and it is - located at the bindings folder. + The files on this journal are prefixed as `activemq-bindings`. Each + file has a `bindings` extension. File size is `1048576`, and it is + located at the bindings folder. -- Message journal. +- Message journal. - This journal instance stores all message related data, including the - message themselves and also duplicate-id caches. + This journal instance stores all message related data, including the + message themselves and also duplicate-id caches. - By default Apache ActiveMQ Artemis will try and use an AIO journal. If AIO is not - available, e.g. the platform is not Linux with the correct kernel - version or AIO has not been installed then it will automatically - fall back to using Java NIO which is available on any Java platform. + By default Apache ActiveMQ Artemis will try and use an AIO journal. If AIO is not + available, e.g. the platform is not Linux with the correct kernel + version or AIO has not been installed then it will automatically + fall back to using Java NIO which is available on any Java platform. - The files on this journal are prefixed as `activemq-data`. Each file - has a `amq` extension. File size is by the default `10485760` - (configurable), and it is located at the journal folder. + The files on this journal are prefixed as `activemq-data`. Each file + has a `amq` extension. File size is by the default `10485760` + (configurable), and it is located at the journal folder. For large messages, Apache ActiveMQ Artemis persists them outside the message journal. This is discussed in [Large Messages](large-messages.md). @@ -132,17 +132,17 @@ the broker for Zero Persistence section. The bindings journal is configured using the following attributes in `broker.xml` -- `bindings-directory` +- `bindings-directory` - This is the directory in which the bindings journal lives. The - default value is `data/bindings`. + This is the directory in which the bindings journal lives. The + default value is `data/bindings`. -- `create-bindings-dir` +- `create-bindings-dir` - If this is set to `true` then the bindings directory will be - automatically created at the location specified in - `bindings-directory` if it does not already exist. The default value - is `true` + If this is set to `true` then the bindings directory will be + automatically created at the location specified in + `bindings-directory` if it does not already exist. The default value + is `true` #### Configuring the jms journal @@ -153,159 +153,158 @@ The jms config shares its configuration with the bindings journal. The message journal is configured using the following attributes in `broker.xml` -- `journal-directory` +- `journal-directory` - This is the directory in which the message journal lives. The - default value is `data/journal`. + This is the directory in which the message journal lives. The + default value is `data/journal`. - For the best performance, we recommend the journal is located on its - own physical volume in order to minimise disk head movement. If the - journal is on a volume which is shared with other processes which - might be writing other files (e.g. bindings journal, database, or - transaction coordinator) then the disk head may well be moving - rapidly between these files as it writes them, thus drastically - reducing performance. + For the best performance, we recommend the journal is located on its + own physical volume in order to minimise disk head movement. If the + journal is on a volume which is shared with other processes which + might be writing other files (e.g. bindings journal, database, or + transaction coordinator) then the disk head may well be moving + rapidly between these files as it writes them, thus drastically + reducing performance. - When the message journal is stored on a SAN we recommend each - journal instance that is stored on the SAN is given its own LUN - (logical unit). + When the message journal is stored on a SAN we recommend each + journal instance that is stored on the SAN is given its own LUN + (logical unit). -- `create-journal-dir` +- `create-journal-dir` - If this is set to `true` then the journal directory will be - automatically created at the location specified in - `journal-directory` if it does not already exist. The default value - is `true` + If this is set to `true` then the journal directory will be + automatically created at the location specified in + `journal-directory` if it does not already exist. The default value + is `true` -- `journal-type` +- `journal-type` - Valid values are `NIO`, `ASYNCIO` or `MAPPED`. + Valid values are `NIO`, `ASYNCIO` or `MAPPED`. - Choosing `NIO` chooses the Java NIO journal. Choosing `ASYNCIO` chooses - the Linux asynchronous IO journal. If you choose `ASYNCIO` but are not - running Linux or you do not have libaio installed then Apache ActiveMQ Artemis will - detect this and automatically fall back to using `NIO`. - Choosing `MAPPED` chooses the Java Memory Mapped journal. + Choosing `NIO` chooses the Java NIO journal. Choosing `ASYNCIO` chooses + the Linux asynchronous IO journal. If you choose `ASYNCIO` but are not + running Linux or you do not have libaio installed then Apache ActiveMQ Artemis will + detect this and automatically fall back to using `NIO`. + Choosing `MAPPED` chooses the Java Memory Mapped journal. -- `journal-sync-transactional` +- `journal-sync-transactional` - If this is set to true then Apache ActiveMQ Artemis will make sure all transaction - data is flushed to disk on transaction boundaries (commit, prepare - and rollback). The default value is `true`. + If this is set to true then Apache ActiveMQ Artemis will make sure all transaction + data is flushed to disk on transaction boundaries (commit, prepare + and rollback). The default value is `true`. -- `journal-sync-non-transactional` +- `journal-sync-non-transactional` - If this is set to true then Apache ActiveMQ Artemis will make sure non - transactional message data (sends and acknowledgements) are flushed - to disk each time. The default value for this is `true`. + If this is set to true then Apache ActiveMQ Artemis will make sure non + transactional message data (sends and acknowledgements) are flushed + to disk each time. The default value for this is `true`. -- `journal-file-size` +- `journal-file-size` - The size of each journal file in bytes. The default value for this - is `10485760` bytes (10MiB). + The size of each journal file in bytes. The default value for this + is `10485760` bytes (10MiB). -- `journal-min-files` +- `journal-min-files` - The minimum number of files the journal will maintain. When Apache ActiveMQ Artemis - starts and there is no initial message data, Apache ActiveMQ Artemis will - pre-create `journal-min-files` number of files. + The minimum number of files the journal will maintain. When Apache ActiveMQ Artemis + starts and there is no initial message data, Apache ActiveMQ Artemis will + pre-create `journal-min-files` number of files. - Creating journal files and filling them with padding is a fairly - expensive operation and we want to minimise doing this at run-time - as files get filled. By pre-creating files, as one is filled the - journal can immediately resume with the next one without pausing to - create it. + Creating journal files and filling them with padding is a fairly + expensive operation and we want to minimise doing this at run-time + as files get filled. By pre-creating files, as one is filled the + journal can immediately resume with the next one without pausing to + create it. - Depending on how much data you expect your queues to contain at - steady state you should tune this number of files to match that - total amount of data. + Depending on how much data you expect your queues to contain at + steady state you should tune this number of files to match that + total amount of data. -- `journal-pool-files` +- `journal-pool-files` - The system will create as many files as needed however when reclaiming files - it will shrink back to the `journal-pool-files`. + The system will create as many files as needed however when reclaiming files + it will shrink back to the `journal-pool-files`. - The default to this parameter is -1, meaning it will never delete files on the journal once created. + The default to this parameter is -1, meaning it will never delete files on the journal once created. - Notice that the system can't grow infinitely as you are still required to use paging for destinations that can - grow indefinitely. + Notice that the system can't grow infinitely as you are still required to use paging for destinations that can + grow indefinitely. - Notice: in case you get too many files you can use [compacting](tools.md). + Notice: in case you get too many files you can use [compacting](data-tools.md). -- `journal-max-io` +- `journal-max-io` - Write requests are queued up before being submitted to the system - for execution. This parameter controls the maximum number of write - requests that can be in the IO queue at any one time. If the queue - becomes full then writes will block until space is freed up. + Write requests are queued up before being submitted to the system + for execution. This parameter controls the maximum number of write + requests that can be in the IO queue at any one time. If the queue + becomes full then writes will block until space is freed up. - When using NIO, this value should always be equal to `1` + When using NIO, this value should always be equal to `1` - When using AIO, the default should be `500`. + When using ASYNCIO, the default should be `500`. - The system maintains different defaults for this parameter depending - on whether it's NIO or AIO (default for NIO is 1, default for AIO is - 500) + The system maintains different defaults for this parameter depending + on whether it's NIO or ASYNCIO (default for NIO is 1, default for ASYNCIO is + 500) - There is a limit and the total max AIO can't be higher than what is - configured at the OS level (/proc/sys/fs/aio-max-nr) usually at - 65536. + There is a limit and the total max ASYNCIO can't be higher than what is + configured at the OS level (/proc/sys/fs/aio-max-nr) usually at + 65536. -- `journal-buffer-timeout` +- `journal-buffer-timeout` - Instead of flushing on every write that requires a flush, we - maintain an internal buffer, and flush the entire buffer either when - it is full, or when a timeout expires, whichever is sooner. This is - used for both NIO and AIO and allows the system to scale better with - many concurrent writes that require flushing. + Instead of flushing on every write that requires a flush, we + maintain an internal buffer, and flush the entire buffer either when + it is full, or when a timeout expires, whichever is sooner. This is + used for both NIO and ASYNCIO and allows the system to scale better with + many concurrent writes that require flushing. - This parameter controls the timeout at which the buffer will be - flushed if it hasn't filled already. AIO can typically cope with a - higher flush rate than NIO, so the system maintains different - defaults for both NIO and AIO (default for NIO is 3333333 - nanoseconds - 300 times per second, default for AIO is 500000 - nanoseconds - ie. 2000 times per second). + This parameter controls the timeout at which the buffer will be + flushed if it hasn't filled already. ASYNCIO can typically cope with a + higher flush rate than NIO, so the system maintains different + defaults for both NIO and ASYNCIO (default for NIO is 3333333 + nanoseconds - 300 times per second, default for ASYNCIO is 500000 + nanoseconds - ie. 2000 times per second). - > **Note** - > - > By increasing the timeout, you may be able to increase system - > throughput at the expense of latency, the default parameters are - > chosen to give a reasonable balance between throughput and - > latency. + > **Note:** + > + > By increasing the timeout, you may be able to increase system + > throughput at the expense of latency, the default parameters are + > chosen to give a reasonable balance between throughput and + > latency. -- `journal-buffer-size` +- `journal-buffer-size` - The size of the timed buffer on AIO. The default value is `490KiB`. + The size of the timed buffer on ASYNCIO. The default value is `490KiB`. -- `journal-compact-min-files` +- `journal-compact-min-files` - The minimal number of files before we can consider compacting the - journal. The compacting algorithm won't start until you have at - least `journal-compact-min-files` + The minimal number of files before we can consider compacting the + journal. The compacting algorithm won't start until you have at + least `journal-compact-min-files` - Setting this to 0 will disable the feature to compact completely. - This could be dangerous though as the journal could grow indefinitely. - Use it wisely! + Setting this to 0 will disable the feature to compact completely. + This could be dangerous though as the journal could grow indefinitely. + Use it wisely! + The default for this parameter is `10` - The default for this parameter is `10` +- `journal-compact-percentage` -- `journal-compact-percentage` + The threshold to start compacting. When less than this percentage is + considered live data, we start compacting. Note also that compacting + won't kick in until you have at least `journal-compact-min-files` + data files on the journal - The threshold to start compacting. When less than this percentage is - considered live data, we start compacting. Note also that compacting - won't kick in until you have at least `journal-compact-min-files` - data files on the journal - - The default for this parameter is `30` - -- `journal-datasync` (default: true) - - This will disable the use of fdatasync on journal writes. - When enabled it ensures full power failure durability, otherwise - process failure durability on journal writes (OS guaranteed). - This is particular effective for `NIO` and `MAPPED` journals, which rely on - *fsync*/*msync* to force write changes to disk. + The default for this parameter is `30` + +- `journal-datasync` (default: true) + + This will disable the use of fdatasync on journal writes. + When enabled it ensures full power failure durability, otherwise + process failure durability on journal writes (OS guaranteed). + This is particular effective for `NIO` and `MAPPED` journals, which rely on + *fsync*/*msync* to force write changes to disk. #### Note on disabling `journal-datasync` @@ -362,9 +361,9 @@ The message journal is configured using the following attributes in The Java NIO journal gives great performance, but If you are running Apache ActiveMQ Artemis using Linux Kernel 2.6 or later, we highly recommend you use -the `AIO` journal for the very best persistence performance. +the `ASYNCIO` journal for the very best persistence performance. -It's not possible to use the AIO journal under other operating systems +It's not possible to use the ASYNCIO journal under other operating systems or earlier versions of the Linux kernel. If you are running Linux kernel 2.6 or later and don't already have @@ -372,11 +371,15 @@ If you are running Linux kernel 2.6 or later and don't already have Using yum, (e.g. on Fedora or Red Hat Enterprise Linux): - yum install libaio +```sh +yum install libaio +``` Using aptitude, (e.g. on Ubuntu or Debian system): - apt-get install libaio +```sh +apt-get install libaio +``` ## JDBC Persistence @@ -423,50 +426,50 @@ To configure Apache ActiveMQ Artemis to use a database for persisting messages a ``` -- `jdbc-connection-url` +- `jdbc-connection-url` - The full JDBC connection URL for your database server. The connection url should include all configuration parameters and database name. Note: When configuring the server using the XML configuration files please ensure to escape any illegal chars; "&" for example, is typical in JDBC connection url and should be escaped to "&". + The full JDBC connection URL for your database server. The connection url should include all configuration parameters and database name. **Note:** When configuring the server using the XML configuration files please ensure to escape any illegal chars; "&" for example, is typical in JDBC connection url and should be escaped to "&". -- `bindings-table-name` +- `bindings-table-name` - The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. -- `message-table-name` +- `message-table-name` - The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + The name of the table in which bindings data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. -- `large-message-table-name` +- `large-message-table-name` - The name of the table in which messages and related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. - -- `page-store-table-name` + The name of the table in which messages and related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + +- `page-store-table-name` - The name of the table to house the page store directory information. Note that each address will have it's own page table which will use this name appended with a unique id of up to 20 characters. + The name of the table to house the page store directory information. Note that each address will have it's own page table which will use this name appended with a unique id of up to 20 characters. -- `node-manager-store-table-name` +- `node-manager-store-table-name` - The name of the table in which the HA Shared Store locks (ie live and backup) and HA related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. - Each Shared Store live/backup pairs must use the same table name and isn't supported to share the same table between multiple (and unrelated) live/backup pairs. + The name of the table in which the HA Shared Store locks (ie live and backup) and HA related data will be persisted for the ActiveMQ Artemis server. Specifying table names allows users to share single database amongst multiple servers, without interference. + Each Shared Store live/backup pairs must use the same table name and isn't supported to share the same table between multiple (and unrelated) live/backup pairs. -- `jdbc-driver-class-name` +- `jdbc-driver-class-name` - The fully qualified class name of the desired database Driver. + The fully qualified class name of the desired database Driver. -- `jdbc-network-timeout` +- `jdbc-network-timeout` - The JDBC network connection timeout in milliseconds. The default value - is 20000 milliseconds (ie 20 seconds). - When using a shared store it is recommended to set it less then or equal to `jdbc-lock-expiration`. + The JDBC network connection timeout in milliseconds. The default value + is 20000 milliseconds (ie 20 seconds). + When using a shared store it is recommended to set it less then or equal to `jdbc-lock-expiration`. -- `jdbc-lock-renew-period` +- `jdbc-lock-renew-period` - The period in milliseconds of the keep alive service of a JDBC lock. The default value - is 2000 milliseconds (ie 2 seconds). - -- `jdbc-lock-expiration` + The period in milliseconds of the keep alive service of a JDBC lock. The default value + is 2000 milliseconds (ie 2 seconds). + +- `jdbc-lock-expiration` - The time in milliseconds a JDBC lock is considered valid without keeping it alive. The default value - is 20000 milliseconds (ie 20 seconds). + The time in milliseconds a JDBC lock is considered valid without keeping it alive. The default value + is 20000 milliseconds (ie 20 seconds). - `jdbc-journal-sync-period` diff --git a/docs/user-manual/en/pre-acknowledge.md b/docs/user-manual/en/pre-acknowledge.md index d328d8b880c..fc7942ad2b5 100644 --- a/docs/user-manual/en/pre-acknowledge.md +++ b/docs/user-manual/en/pre-acknowledge.md @@ -2,11 +2,11 @@ JMS specifies 3 acknowledgement modes: -- `AUTO_ACKNOWLEDGE` +- `AUTO_ACKNOWLEDGE` -- `CLIENT_ACKNOWLEDGE` +- `CLIENT_ACKNOWLEDGE` -- `DUPS_OK_ACKNOWLEDGE` +- `DUPS_OK_ACKNOWLEDGE` Apache ActiveMQ Artemis supports two additional modes: `PRE_ACKNOWLEDGE` and `INDIVIDUAL_ACKNOWLEDGE` @@ -32,7 +32,7 @@ update messages. With these messages it might be reasonable to lose a message in event of crash, since the next price update message will arrive soon, overriding the previous price. -> **Note** +> **Note:** > > Please note, that if you use pre-acknowledge mode, then you will lose > transactional semantics for messages being consumed, since clearly @@ -67,7 +67,7 @@ acknowledge mode with `ActiveMQJMSConstants.INDIVIDUAL_ACKNOWLEDGE`. Individual ACK inherits all the semantics from Client Acknowledge, with the exception the message is individually acked. -> **Note** +> **Note:** > > Please note, that to avoid confusion on MDB processing, Individual > ACKNOWLEDGE is not supported through MDBs (or the inbound resource @@ -76,5 +76,5 @@ the exception the message is individually acked. ## Example -See the [examples](examples.md) chapter for an example which shows how to -use pre-acknowledgement mode with JMS. +See the [Pre-acknowledge Example](examples.md#pre-acknowledge) which shows how +to use pre-acknowledgement mode with JMS. diff --git a/docs/user-manual/en/preface.md b/docs/user-manual/en/preface.md index 6dccb7e8d8c..cbcc7d98e04 100644 --- a/docs/user-manual/en/preface.md +++ b/docs/user-manual/en/preface.md @@ -2,43 +2,43 @@ What is Apache ActiveMQ Artemis? -- Apache ActiveMQ Artemis is an open source project to build a multi-protocol, - embeddable, very high performance, clustered, asynchronous messaging - system. +- Apache ActiveMQ Artemis is an open source project to build a multi-protocol, + embeddable, very high performance, clustered, asynchronous messaging + system. -- Apache ActiveMQ Artemis is an example of Message Oriented Middleware (MoM). For a - description of MoMs and other messaging concepts please see the [Messaging Concepts](messaging-concepts.md). +- Apache ActiveMQ Artemis is an example of Message Oriented Middleware (MoM). For a + description of MoMs and other messaging concepts please see the [Messaging Concepts](messaging-concepts.md). Why use Apache ActiveMQ Artemis? Here are just a few of the reasons: -- 100% open source software. Apache ActiveMQ Artemis is licensed using the Apache - Software License v 2.0 to minimise barriers to adoption. +- 100% open source software. Apache ActiveMQ Artemis is licensed using the Apache + Software License v 2.0 to minimise barriers to adoption. -- Apache ActiveMQ Artemis is designed with usability in mind. +- Apache ActiveMQ Artemis is designed with usability in mind. -- Written in Java. Runs on any platform with a Java 8+ runtime, that's - everything from Windows desktops to IBM mainframes. +- Written in Java. Runs on any platform with a Java 8+ runtime, that's + everything from Windows desktops to IBM mainframes. -- Amazing performance. Our ground-breaking high performance journal - provides persistent messaging performance at rates normally seen for - non-persistent messaging, our non-persistent messaging performance - rocks the boat too. +- Amazing performance. Our ground-breaking high performance journal + provides persistent messaging performance at rates normally seen for + non-persistent messaging, our non-persistent messaging performance + rocks the boat too. -- Full feature set. All the features you'd expect in any serious - messaging system, and others you won't find anywhere else. +- Full feature set. All the features you'd expect in any serious + messaging system, and others you won't find anywhere else. -- Elegant, clean-cut design with minimal third party dependencies. Run - ActiveMQ Artemis stand-alone, run it in integrated in your favourite Java EE - application server, or run it embedded inside your own product. It's - up to you. +- Elegant, clean-cut design with minimal third party dependencies. Run + ActiveMQ Artemis stand-alone, run it in integrated in your favourite Java EE + application server, or run it embedded inside your own product. It's + up to you. -- Seamless high availability. We provide a HA solution with automatic - client failover so you can guarantee zero message loss or - duplication in event of server failure. +- Seamless high availability. We provide a HA solution with automatic + client failover so you can guarantee zero message loss or + duplication in event of server failure. -- Hugely flexible clustering. Create clusters of servers that know how - to load balance messages. Link geographically distributed clusters - over unreliable connections to form a global network. Configure - routing of messages in a highly flexible way. +- Hugely flexible clustering. Create clusters of servers that know how + to load balance messages. Link geographically distributed clusters + over unreliable connections to form a global network. Configure + routing of messages in a highly flexible way. diff --git a/docs/user-manual/en/project-info.md b/docs/user-manual/en/project-info.md index 3dc4b72eeec..a952c985ca5 100644 --- a/docs/user-manual/en/project-info.md +++ b/docs/user-manual/en/project-info.md @@ -9,20 +9,19 @@ page: ## Project Information -- If you have any user questions please use our [user - forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-User-f2341805.html) +- If you have any user questions please use our [user + forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-User-f2341805.html) -- If you have development related questions, please use our [developer - forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-Dev-f2368404.html) +- If you have development related questions, please use our [developer + forum](http://activemq.2283324.n4.nabble.com/ActiveMQ-Dev-f2368404.html) -- Pop in and chat to us in our [IRC - channel](irc://irc.freenode.net:6667/apache-activemq) +- Pop in and chat to us in our [IRC + channel](irc://irc.freenode.net:6667/apache-activemq) -- Apache ActiveMQ Artemis Git repository is - -- All release tags are available from - +- Apache ActiveMQ Artemis Git repository is +- All release tags are available from + And many thanks to all our contributors, both old and new who helped create Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/protocols-interoperability.md b/docs/user-manual/en/protocols-interoperability.md index 3a6a26aeb11..0fe16f76e2e 100644 --- a/docs/user-manual/en/protocols-interoperability.md +++ b/docs/user-manual/en/protocols-interoperability.md @@ -1,646 +1,69 @@ # Protocols and Interoperability -## Protocols +Apache ActiveMQ Artemis has a powerful & flexible core which provides a foundation upon which other protocols can be +implemented. Each protocol implementation translates the ideas of its specific protocol onto this core. -ActiveMQ Artemis has a plugable protocol architecture. Protocol plugins come in the form of ActiveMQ Artemis protocol -modules. Each protocol module should be added to the brokers class path and are loaded by the broker at boot time. -ActiveMQ Artemis ships with 5 protocol modules out of the box. The 5 modules offer support for the following protocols: +The broker ships with a client implementation which interacts directly with this core. It uses what's called the ["core" +API](core.md), and it communicates over the network using the "core" protocol. -* AMQP -* OpenWire -* MQTT -* STOMP -* HornetQ +## Supported Protocols & APIs -In addition to the protocols above ActiveMQ Artemis also offers support for it's own highly performant native protocol - "Core". +The broker has a pluggable protocol architecture. Protocol plugins come in the form of protocol modules. Each protocol +module is included on the broker's class path and loaded by the broker at boot time. The broker ships with 5 protocol +modules out of the box. The 5 modules offer support for the following protocols: -## Configuring protocols +- [AMQP](amqp.md) +- [OpenWire](openwire.md) +- [MQTT](mqtt.md) +- [STOMP](stomp.md) +- HornetQ + +#### APIs and Other Interfaces + +Although JMS is a standardized API, it does not define a network protocol. The [ActiveMQ Artemis JMS 2.0 client](using-jms.md) +is implemented on top of the core protocol. We also provide a [client-side JNDI implementation](using-jms.md#jndi). + +The broker also ships with a [REST messaging interface](rest.md) (not to be confused with the REST management API +provided via our integration with Jolokia). + +## Configuring Acceptors In order to make use of a particular protocol, a transport must be configured with the desired protocol enabled. There is a whole section on configuring transports that can be found [here](configuring-transports.md). The default configuration shipped with the ActiveMQ Artemis distribution comes with a number of acceptors already -defined, one for each of the above protocols plus a generic acceptor that supports all protocols. To enable a -protocol on a particular acceptor simply add a url parameter "protocol=AMQP,STOMP" to the acceptor url. Where the value -of the parameter is a comma separated list of protocol names. If the protocol parameter is omitted from the url all -protocols are enabled. +defined, one for each of the above protocols plus a generic acceptor that supports all protocols. To enable +protocols on a particular acceptor simply add the `protocols` url parameter to the acceptor url where the value is one +or more protocols (separated by commas). If the `protocols` parameter is omitted from the url **all** protocols are +enabled. +- The following example enables only MQTT on port 1883 ```xml - tcp://localhost:1883?protocols=MQTT +``` - +- The following example enables MQTT and AMQP on port 1883 +```xml - tcp://localhost:1883?protocols=MQTT,AMQP + tcp://localhost:5672?protocols=MQTT,AMQP +``` - +- The following example enables **all** protocols on `61616`: +```xml tcp://localhost:61616 ``` -## AMQP - -Apache ActiveMQ Artemis supports the [AMQP -1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp) -specification. To enable AMQP you must configure a Netty Acceptor to -receive AMQP clients, like so: - -```xml -tcp://localhost:5672?protocols=AMQP -``` - - -Apache ActiveMQ Artemis will then accept AMQP 1.0 clients on port 5672 which is the -default AMQP port. - -There are 2 AMQP examples available see proton-j and proton-ruby which -use the qpid Java and Ruby clients respectively. - -### AMQP and security - -The Apache ActiveMQ Artemis Server accepts AMQP SASL Authentication and will use this -to map onto the underlying session created for the connection so you can -use the normal Apache ActiveMQ Artemis security configuration. - -### AMQP Links - -An AMQP Link is a uni directional transport for messages between a -source and a target, i.e. a client and the Apache ActiveMQ Artemis Broker. A link will -have an endpoint of which there are 2 kinds, a Sender and A Receiver. At -the Broker a Sender will have its messages converted into an Apache ActiveMQ Artemis -Message and forwarded to its destination or target. A Receiver will map -onto an Apache ActiveMQ Artemis Server Consumer and convert Apache ActiveMQ Artemis messages back into -AMQP messages before being delivered. - -### AMQP and destinations - -If an AMQP Link is dynamic then a temporary queue will be created and -either the remote source or remote target address will be set to the -name of the temporary queue. If the Link is not dynamic then the the -address of the remote target or source will used for the queue. If this -does not exist then an exception will be sent - -> **Note** -> -> For the next version we will add a flag to aut create durable queue -> but for now you will have to add them via the configuration - -### AMQP and Multicast Queues (Topics) - -Although amqp has no notion of topics it is still possible to treat amqp consumers or receivers as subscriptions rather -than just consumers on a queue. By default any receiving link that attaches to an address that has only multicast enabled -will be treated as a subscription and a subscription queue will be created. If the Terminus Durability is either UNSETTLED_STATE -or CONFIGURATION then the queue will be made durable, similar to a JMS durable subscription and given a name made up from -the container id and the link name, something like `my-container-id:my-link-name`. if the Terminus Durability is configured -as NONE then a volatile multicast queue will be created. - -Artemis also supports the qpid-jms client and will respect its use of topics regardless of the prefix used for the address. - -### AMQP and Coordinations - Handling Transactions - -An AMQP links target can also be a Coordinator, the Coordinator is used -to handle transactions. If a coordinator is used the the underlying -HormetQ Server session will be transacted and will be either rolled back -or committed via the coordinator. - -> **Note** -> -> AMQP allows the use of multiple transactions per session, -> `amqp:multi-txns-per-ssn`, however in this version Apache ActiveMQ Artemis will only -> support single transactions per session - -### AMQP scheduling message delivery - -An AMQP message can provide scheduling information that controls the time in the future when the -message will be delivered at the earliest. This information is provided by adding a message annotation -to the sent message. - -There are two different message annotations that can be used to schedule a message for later delivery: - -* `x-opt-delivery-time` -The specified value must be a positive long corresponding to the time the message should be made available -for delivery (in milliseconds). - -* `x-opt-delivery-delay` -The specified value must be a positive long corresponding to the amount of milliseconds after the broker -receives the given message before it should be made available for delivery. - -if both annotations are present in the same message then the broker will prefer the more specific `x-opt-delivery-time` value. - -## OpenWire - -Apache ActiveMQ Artemis now supports the -[OpenWire](http://activemq.apache.org/openwire.html) protocol so that an -Apache ActiveMQ 5.x JMS client can talk directly to an Apache ActiveMQ Artemis server. To enable -OpenWire support you must configure a Netty Acceptor, like so: - -```xml -tcp://localhost:61616?protocols=OPENWIRE -``` - -The Apache ActiveMQ Artemis server will then listens on port 61616 for incoming -openwire commands. Please note the "protocols" is not mandatory here. -The openwire configuration conforms to Apache ActiveMQ Artemis's "Single Port" feature. -Please refer to [Configuring Single Port](configuring-transports.md#single-port-support) for details. - -Please refer to the openwire example for more coding details. - -Currently we support Apache ActiveMQ Artemis clients that using standard JMS APIs. In -the future we will get more supports for some advanced, Apache ActiveMQ Artemis -specific features into Apache ActiveMQ Artemis. - -### Connection Monitoring - -OpenWire has a few parameters to control how each connection is monitored, they are: - -* maxInactivityDuration: -It specifies the time (milliseconds) after which the connection is closed by the broker if no data was received. -Default value is 30000. - -* maxInactivityDurationInitalDelay: -It specifies the maximum delay (milliseconds) before inactivity monitoring is started on the connection. -It can be useful if a broker is under load with many connections being created concurrently. -Default value is 10000. - -* useInactivityMonitor: -A value of false disables the InactivityMonitor completely and connections will never time out. -By default it is enabled. On broker side you don't neet set this. Instead you can set the -connection-ttl to -1. - -* useKeepAlive: -Whether or not to send a KeepAliveInfo on an idle connection to prevent it from timing out. -Enabled by default. Disabling the keep alive will still make connections time out if no data -was received on the connection for the specified amount of time. - -Note at the beginning the InactivityMonitor negotiates the appropriate maxInactivityDuration and -maxInactivityDurationInitalDelay. The shortest duration is taken for the connection. - -More details please see [ActiveMQ InactivityMonitor](http://activemq.apache.org/activemq-inactivitymonitor.html). - -### Disable/Enable Advisories - -By default, advisory topics ([ActiveMQ Advisory](http://activemq.apache.org/advisory-message.html)) -are created in order to send certain type of advisory messages to listening clients. As a result, -advisory addresses and queues will be displayed on the management console, along with user deployed -addresses and queues. This sometimes cause confusion because the advisory objects are internally -managed without user being aware of them. In addition, users may not want the advisory topics at all -(they cause extra resources and performance penalty) and it is convenient to disable them at all -from the broker side. - -The protocol provides two parameters to control advisory behaviors on the broker side. - -* supportAdvisory -Whether or not the broker supports advisory messages. If the value is true, advisory addresses/ -queues will be created. If the value is false, no advisory addresses/queues are created. Default -value is true. - -* suppressInternalManagementObjects -Whether or not the advisory addresses/queues, if any, will be registered to management service -(e.g. JMX registry). If set to true, no advisory addresses/queues will be registered. If set to -false, those are registered and will be displayed on the management console. Default value is -true. - -The two parameters are configured on openwire acceptors, via URLs or API. For example: - - tcp://127.0.0.1:61616?protocols=CORE,AMQP,OPENWIRE;supportAdvisory=true;suppressInternalManagementObjects=false - -### Virtual Topic Consumer Destination Translation - -For existing OpenWire consumers of virtual topic destinations it is possible to configure a mapping function -that will translate the virtual topic consumer destination into a FQQN address. This address then represents -the consumer as a multicast binding to an address representing the virtual topic. - -The configuration string property ```virtualTopicConsumerWildcards``` has two parts seperated by a ```;```. -The first is the 5.x style destination filter that identifies the destination as belonging to a virtual topic. -The second identifies the number of ```paths``` that identify the consumer queue such that it can be parsed from the -destination. -For example, the default 5.x virtual topic with consumer prefix of ```Consumer.*.```, would require a -```virtualTopicConsumerWildcards``` filter of ```Consumer.*.>;2```. As a url parameter this transforms to ```Consumer.*.%3E%3B2``` when -the url significant characters ```>;``` are escaped with their hex code points. -In an acceptor url it would be: - -```xml -tcp://127.0.0.1:61616?protocols=OPENWIRE;virtualTopicConsumerWildcards=Consumer.*.%3E%3B2 -``` - -This will translate ```Consumer.A.VirtualTopic.Orders``` into a FQQN of ```VirtualTopic.Orders::Consumer.A``` using the -int component ```2``` of the configuration to identify the consumer queue as the first two paths of the destination. -```virtualTopicConsumerWildcards``` is multi valued using a ```,``` separator. - -Please see Virtual Topic Mapping example contained in the OpenWire [examples](examples.md). - -## MQTT - -MQTT is a light weight, client to server, publish / subscribe messaging protocol. MQTT has been specifically -designed to reduce transport overhead (and thus network traffic) and code footprint on client devices. -For this reason MQTT is ideally suited to constrained devices such as sensors and actuators and is quickly -becoming the defacto standard communication protocol for IoT. - -Apache ActiveMQ Artemis supports MQTT v3.1.1 (and also the older v3.1 code message format). To enable MQTT, -simply add an appropriate acceptor with the MQTT protocol enabled. For example: - - tcp://localhost:1883?protocols=MQTT - -By default the configuration shipped with Apache ActiveMQ Artemis has the above acceptor already defined, MQTT is -also active by default on the generic acceptor defined on port 61616 (where all protocols are enabled), in the out -of the box configuration. - -The best source of information on the MQTT protocol is in the specification. The MQTT v3.1.1 specification can -be downloaded from the OASIS website here: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html - -Some note worthy features of MQTT are explained below: - -### MQTT Quality of Service - -MQTT offers 3 quality of service levels. - -Each message (or topic subscription) can define a quality of service that is associated with it. The quality of service -level defined on a topic is the maximum level a client is willing to accept. The quality of service level on a -message is the desired quality of service level for this message. The broker will attempt to deliver messages to -subscribers at the highest quality of service level based on what is defined on the message and topic subscription. - -Each quality of service level offers a level of guarantee by which a message is sent or received: - -* QoS 0: AT MOST ONCE: Guarantees that a particular message is only ever received by the subscriber a maximum of one time. -This does mean that the message may never arrive. The sender and the receiver will attempt to deliver the message, -but if something fails and the message does not reach it's destination (say due to a network connection) the message -may be lost. This QoS has the least network traffic overhead and the least burden on the client and the broker and is often -useful for telemetry data where it doesn't matter if some of the data is lost. - -* QoS 1: AT LEAST ONCE: Guarantees that a message will reach it's intended recipient one or more times. The sender will -continue to send the message until it receives an acknowledgment from the recipient, confirming it has received the message. -The result of this QoS is that the recipient may receive the message multiple times, and also increases the network -overhead than QoS 0, (due to acks). In addition more burden is placed on the sender as it needs to store the message -and retry should it fail to receive an ack in a reasonable time. - -* QoS 2: EXACTLY ONCE: The most costly of the QoS (in terms of network traffic and burden on sender and receiver) this -QoS will ensure that the message is received by a recipient exactly one time. This ensures that the receiver never gets -any duplicate copies of the message and will eventually get it, but at the extra cost of network overhead and complexity -required on the sender and receiver. - -### MQTT Retain Messages - -MQTT has an interesting feature in which messages can be "retained" for a particular address. This means that once a -retain message has been sent to an address, any new subscribers to that address will receive the last sent retain -message before any others messages, this happens even if the retained message was sent before a client has connected -or subscribed. An example of where this feature might be useful is in environments such as IoT where devices need to -quickly get the current state of a system when they are on boarded into a system. - -### Will Messages - -A will message can be sent when a client initially connects to a broker. Clients are able to set a "will -message" as part of the connect packet. If the client abnormally disconnects, say due to a device or network failure -the broker will proceed to publish the will message to the specified address (as defined also in the connect packet). -Other subscribers to the will topic will receive the will message and can react accordingly. This feature can be useful - in an IoT style scenario to detect errors across a potentially large scale deployment of devices. - -### Debug Logging - -Detailed protocol logging (e.g. packets in/out) can be activated via the following steps: - -1) Open `/etc/logging.properties` -2) Add `org.apache.activemq.artemis.core.protocol.mqtt` to the `loggers` list. -3) Add this line to enable `TRACE` logging for this new logger: `logger.org.apache.activemq.artemis.core.protocol.mqtt.level=TRACE` -4) Ensure the `level` for the `handler` you want to log the message doesn't block the `TRACE` logging. For example, - modify the `level` of the `CONSOLE` `handler` like so: `handler.CONSOLE.level=TRACE` - -The MQTT specification doesn't dictate the format of the payloads which clients publish. As far as the broker is -concerned a payload is just just an array of bytes. However, to facilitate logging the broker will encode the payloads -as UTF-8 strings and print them up to 256 characters. Payload logging is limited to avoid filling the logs with potentially -hundreds of megabytes of unhelpful information. - - -### Wild card subscriptions - -MQTT addresses are hierarchical much like a file system, and use "/" character to separate hierarchical levels. -Subscribers are able to subscribe to specific topics or to whole branches of a hierarchy. - -To subscribe to branches of an address hierarchy a subscriber can use wild cards. - -There are 2 types of wild card in MQTT: - - * "#" Multi level wild card. Adding this wild card to an address would match all branches of the address hierarchy - under a specified node. For example: /uk/# Would match /uk/cities, /uk/cities/newcastle and also /uk/rivers/tyne. - Subscribing to an address "#" would result in subscribing to all topics in the broker. This can be useful, but should - be done so with care since it has significant performance implications. - - * "+" Single level wild card. Matches a single level in the address hierarchy. For example /uk/+/stores would - match /uk/newcastle/stores but not /uk/cities/newcastle/stores. - - -## Stomp - -[Stomp](https://stomp.github.io/) is a text-orientated wire protocol -that allows Stomp clients to communicate with Stomp Brokers. Apache ActiveMQ Artemis -now supports Stomp 1.0, 1.1 and 1.2. - -Stomp clients are available for several languages and platforms making -it a good choice for interoperability. - -## Native Stomp support - -Apache ActiveMQ Artemis provides native support for Stomp. To be able to send and -receive Stomp messages, you must configure a `NettyAcceptor` with a -`protocols` parameter set to have `stomp`: - -```xml -tcp://localhost:61613?protocols=STOMP -``` - -With this configuration, Apache ActiveMQ Artemis will accept Stomp connections on the -port `61613` (which is the default port of the Stomp brokers). - -See the `stomp` example which shows how to configure an Apache ActiveMQ Artemis server -with Stomp. - -### Limitations - -Message acknowledgements are not transactional. The ACK frame can not be -part of a transaction (it will be ignored if its `transaction` header is -set). - -### Stomp 1.1/1.2 Notes - -#### Virtual Hosting - -Apache ActiveMQ Artemis currently doesn't support virtual hosting, which means the -'host' header in CONNECT fram will be ignored. - -### Mapping Stomp destinations to addresses and queues - -Stomp clients deals with *destinations* when sending messages and -subscribing. Destination names are simply strings which are mapped to -some form of destination on the server - how the server translates these -is left to the server implementation. - -In Apache ActiveMQ Artemis, these destinations are mapped to *addresses* and *queues* -depending on the operation being done and the desired semantics (e.g. anycast or -multicast). - -#### Sending - -When a Stomp client sends a message (using a `SEND` frame), the protocol manager looks -at the message to determine where to route it and potentially how to create the address -and/or queue to which it is being sent. The protocol manager uses either of the following -bits of information from the frame to determine the routing type: - -1. The value of the `destination-type` header. Valid values are `ANYCAST` and -`MULTICAST` (case sensitive). - -2. The "prefix" on the `destination` header. See [additional info](address-model.md) on -prefixes. - -If no indication of routing type is supplied then anycast semantics are used. - -The `destination` header maps to an address of the same name. If the `destination` header -used a prefix then the prefix is stripped. - -#### Receiving - -When a client receives a message from the broker the message will have the `destination-type` -header set to either `MULTICAST` or `ANYCAST` as determined when the message was originally -sent/routed. - -#### Subscribing - -When a Stomp client subscribes to a destination (using a `SUBSCRIBE` frame), the protocol -manager looks at the frame to determine what subscription semantics to use and potentially how -to create the address and/or queue for the subscription. The protocol manager uses either of -the following bits of information from the frame to determine the routing type: - -1. The value of the `subscription-type` header. Valid values are `ANYCAST` and -`MULTICAST` (case sensitive). - -2. The "prefix" on the `destination` header. See [additional info](address-model.md) on -prefixes. - -If no indication of routing type is supplied then anycast semantics are used. - -The `destination` header maps to an address of the same name if multicast is used or to a queue -of the same name if anycast is used. If the `destination` header used a prefix then the prefix -is stripped. - -### STOMP heart-beating and connection-ttl - -Well behaved STOMP clients will always send a DISCONNECT frame before -closing their connections. In this case the server will clear up any -server side resources such as sessions and consumers synchronously. -However if STOMP clients exit without sending a DISCONNECT frame or if -they crash the server will have no way of knowing immediately whether -the client is still alive or not. STOMP connections therefore default to -a connection-ttl value of 1 minute (see chapter on -[connection-ttl](connection-ttl.md) for more information. This value can -be overridden using the `connection-ttl-override` property or if you -need a specific connectionTtl for your stomp connections without -affecting the broker-wide `connection-ttl-override` setting, you can -configure your stomp acceptor with the "connectionTtl" property, which -is used to set the ttl for connections that are created from that acceptor. -For example: - -```xml -tcp://localhost:61613?protocols=STOMP;connectionTtl=20000 -``` - -The above configuration will make sure that any Stomp connection that is -created from that acceptor and does not include a `heart-beat` header -or disables client-to-server heart-beats by specifying a `0` value will -have its connection-ttl set to 20 seconds. The `connectionTtl` set on an -acceptor will take precedence over `connection-ttl-override`. The default -`connectionTtl` is 60,000 milliseconds. - -Since Stomp 1.0 does not support heart-beating then all connections from -Stomp 1.0 clients will have a connection TTL imposed upon them by the broker -based on the aforementioned configuration options. Likewise, any Stomp 1.1 -or 1.2 clients that don't specify a `heart-beat` header or disable client-to-server -heart-beating (e.g. by sending `0,X` in the `heart-beat` header) will have -a connection TTL imposed upon them by the broker. - -For Stomp 1.1 and 1.2 clients which send a non-zero client-to-server `heart-beat` -header value then their connection TTL will be set accordingly. However, the broker -will not strictly set the connection TTL to the same value as the specified in the -`heart-beat` since even small network delays could then cause spurious disconnects. -Instead, the client-to-server value in the `heart-beat` will be multiplied by the -`heartBeatConnectionTtlModifer` specified on the acceptor. The -`heartBeatConnectionTtlModifer` is a decimal value that defaults to `2.0` so for -example, if a client sends a `heart-beat` header of `1000,0` the the connection TTL -will be set to `2000` so that the data or ping frames sent every 1000 milliseconds will -have a sufficient cushion so as not to be considered late and trigger a disconnect. -This is also in accordance with the Stomp 1.1 and 1.2 specifications which both state, -"because of timing inaccuracies, the receiver SHOULD be tolerant and take into account -an error margin." - -The minimum and maximum connection TTL allowed can also be specified on the -acceptor via the `connectionTtlMin` and `connectionTtlMax` properties respectively. -The default `connectionTtlMin` is 1000 and the default `connectionTtlMax` is Java's -`Long.MAX_VALUE` meaning there essentially is no max connection TTL by default. -Keep in mind that the `heartBeatConnectionTtlModifer` is relevant here. For -example, if a client sends a `heart-beat` header of `20000,0` and the acceptor -is using a `connectionTtlMax` of `30000` and a default `heartBeatConnectionTtlModifer` -of `2.0` then the connection TTL would be `40000` (i.e. `20000` * `2.0`) which would -exceed the `connectionTtlMax`. In this case the server would respond to the client -with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As described -previously, this is to make sure there is a sufficient cushion for the client -heart-beats in accordance with the Stomp 1.1 and 1.2 specifications. The same kind -of calculation is done for `connectionTtlMin`. - -The minimum server-to-client heart-beat value is 500ms. - -> **Note** -> -> Please note that the STOMP protocol version 1.0 does not contain any -> heart-beat frame. It is therefore the user's responsibility to make -> sure data is sent within connection-ttl or the server will assume the -> client is dead and clean up server side resources. With `Stomp 1.1` -> users can use heart-beats to maintain the life cycle of stomp -> connections. - -### Selector/Filter expressions - -Stomp subscribers can specify an expression used to select or filter -what the subscriber receives using the `selector` header. The filter -expression syntax follows the *core filter syntax* described in the -[Filter Expressions](filter-expressions.md) documentation. - -### Stomp and JMS interoperability - -#### Sending and consuming Stomp message from JMS or Apache ActiveMQ Artemis Core API - -Stomp is mainly a text-orientated protocol. To make it simpler to -interoperate with JMS and Apache ActiveMQ Artemis Core API, our Stomp implementation -checks for presence of the `content-length` header to decide how to map -a Stomp 1.0 message to a JMS Message or a Core message. - -If the Stomp 1.0 message does *not* have a `content-length` header, it will -be mapped to a JMS *TextMessage* or a Core message with a *single -nullable SimpleString in the body buffer*. - -Alternatively, if the Stomp 1.0 message *has* a `content-length` header, it -will be mapped to a JMS *BytesMessage* or a Core message with a *byte[] -in the body buffer*. - -The same logic applies when mapping a JMS message or a Core message to -Stomp. A Stomp 1.0 client can check the presence of the `content-length` -header to determine the type of the message body (String or bytes). - -#### Message IDs for Stomp messages - -When receiving Stomp messages via a JMS consumer or a QueueBrowser, the -messages have no properties like JMSMessageID by default. However this -may bring some inconvenience to clients who wants an ID for their -purpose. Apache ActiveMQ Artemis Stomp provides a parameter to enable message ID on -each incoming Stomp message. If you want each Stomp message to have a -unique ID, just set the `stompEnableMessageId` to true. For example: - - tcp://localhost:61613?protocols=STOMP;stompEnableMessageId=true - -When the server starts with the above setting, each stomp message sent -through this acceptor will have an extra property added. The property -key is ` - amq-message-id` and the value is a String representation of a -long type internal message id prefixed with "`STOMP`", like: - - amq-message-id : STOMP12345 - -If `stomp-enable-message-id` is not specified in the configuration, -default is `false`. - -### Durable Subscriptions - -The `SUBSCRIBE` and `UNSUBSCRIBE` frames can be augmented with special headers to create -and destroy durable subscriptions respectively. - -To create a durable subscription the `client-id` header must be set on the `CONNECT` frame -and the `durable-subscription-name` must be set on the `SUBSCRIBE` frame. The combination -of these two headers will form the identity of the durable subscription. - -To delete a durable subscription the `client-id` header must be set on the `CONNECT` frame -and the `durable-subscription-name` must be set on the `UNSUBSCRIBE` frame. The values for -these headers should match what was set on the `SUBSCRIBE` frame to delete the corresponding -durable subscription. - -It is possible to pre-configure durable subscriptions since the Stomp implementation creates -the queue used for the durable subscription in a deterministic way (i.e. using the format of -`client-id`.`subscription-name`). For example, if you wanted to configure a durable -subscription on the address `myAddress` with a client-id of `myclientid` and a subscription -name of `mysubscription` then configure the durable subscription: - -```xml - - ... - -
- - - -
-
- ... -
-``` - -### Handling of Large Messages with Stomp - -Stomp clients may send very large frame bodies which can exceed the -size of Apache ActiveMQ Artemis server's internal buffer, causing unexpected errors. To -prevent this situation from happening, Apache ActiveMQ Artemis provides a stomp -configuration attribute `stompMinLargeMessageSize`. This attribute -can be configured inside a stomp acceptor, as a parameter. For example: - -```xml -tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240 -``` - -The type of this attribute is integer. When this attributed is -configured, Apache ActiveMQ Artemis server will check the size of the body of each -Stomp frame arrived from connections established with this acceptor. If -the size of the body is equal or greater than the value of -`stompMinLargeMessageSize`, the message will be persisted as a large -message. When a large message is delievered to a stomp consumer, the -broker will automatically handle the conversion from a large -message to a normal message, before sending it to the client. - -If a large message is compressed, the server will uncompressed it before -sending it to stomp clients. The default value of -`stompMinLargeMessageSize` is the same as the default value of -[min-large-message-size](large-messages.md#configuring-parameters). - -### Stomp Over Web Sockets - -Apache ActiveMQ Artemis also support Stomp over [Web -Sockets](https://html.spec.whatwg.org/multipage/web-sockets.html). Modern web browser which -support Web Sockets can send and receive Stomp messages from Apache ActiveMQ Artemis. - -Stomp over Web Sockets is supported via the normal Stomp acceptor: - -```xml -tcp://localhost:61614?protocols=STOMP -``` - -With this configuration, Apache ActiveMQ Artemis will accept Stomp connections over Web -Sockets on the port `61614`. Web browser can -then connect to `ws://:61614` using a Web Socket to send -and receive Stomp messages. - -A companion JavaScript library to ease client-side development is -available from [GitHub](https://github.com/jmesnil/stomp-websocket) -(please see its [documentation](http://jmesnil.net/stomp-websocket/doc/) -for a complete description). - -The payload length of websocket frames can vary between client implementations. By default -Apache ActiveMQ Artemis will accept frames with a payload length of 65,536. If the client -needs to send payloads longer than this in a single frame this length can be adjusted by -using the `stompMaxFramePayloadLength` URL parameter on the acceptor. - -The `stomp-websockets` example shows how to configure Apache ActiveMQ Artemis server to -have web browsers and Java applications exchanges messages on a JMS -topic. - -## REST - -Please see [Rest Interface](rest.md) +Here are the supported protocols and their corresponding value used in the `protocols` url parameter. +Protocol|`protocols` value +---|--- +Core (Artemis & HornetQ native)|`CORE` +OpenWire (5.x native)|`OPENWIRE` +AMQP|`AMQP` +MQTT|`MQTT` +STOMP|`STOMP` \ No newline at end of file diff --git a/docs/user-manual/en/resource-limits.md b/docs/user-manual/en/resource-limits.md index 7939dc7293f..45e4d84a8aa 100644 --- a/docs/user-manual/en/resource-limits.md +++ b/docs/user-manual/en/resource-limits.md @@ -20,10 +20,10 @@ Here is an example of the XML used to set resource limits: ``` Unlike the `match` from `address-setting`, this `match` does not use -any wild-card syntax. It's a simple 1:1 mapping of the limits to a user. +any wild-card syntax. It's a simple 1:1 mapping of the limits to a **user**. -`max-connections` defines how many connections the matched user can make +- `max-connections` defines how many connections the matched user can make to the broker. The default is -1 which means there is no limit. -`max-queues` defines how many queues the matched user can create. The default +- `max-queues` defines how many queues the matched user can create. The default is -1 which means there is no limit. \ No newline at end of file diff --git a/docs/user-manual/en/rest.md b/docs/user-manual/en/rest.md index 91d4cd283b0..7915eef0df5 100644 --- a/docs/user-manual/en/rest.md +++ b/docs/user-manual/en/rest.md @@ -10,15 +10,17 @@ and receiving simple HTTP messages that contain the content you want to push aro here's a simple example of posting an order to an order processing queue express as an HTTP message: - POST /queue/orders/create HTTP/1.1 - Host: example.com - Content-Type: application/xml - - - Bill - iPhone 4 - $199.99 - +``` +POST /queue/orders/create HTTP/1.1 +Host: example.com +Content-Type: application/xml + + + Bill + iPhone 4 + $199.99 + +``` As you can see, we're just posting some arbitrary XML document to a URL. When the XML is received on the server is it processed within Apache ActiveMQ Artemis @@ -31,29 +33,29 @@ discuss the entire interface in detail later. Why would you want to use Apache ActiveMQ Artemis's REST interface? What are the goals of the REST interface? -- Easily usable by machine-based (code) clients. +- Easily usable by machine-based (code) clients. -- Zero client footprint. We want Apache ActiveMQ Artemis to be usable by any - client/programming language that has an adequate HTTP client - library. You shouldn't have to download, install, and configure a - special library to interact with Apache ActiveMQ Artemis. +- Zero client footprint. We want Apache ActiveMQ Artemis to be usable by any + client/programming language that has an adequate HTTP client + library. You shouldn't have to download, install, and configure a + special library to interact with Apache ActiveMQ Artemis. -- Lightweight interoperability. The HTTP protocol is strong enough to - be our message exchange protocol. Since interactions are RESTful the - HTTP uniform interface provides all the interoperability you need to - communicate between different languages, platforms, and even - messaging implementations that choose to implement the same RESTful - interface as Apache ActiveMQ Artemis (i.e. the [REST-\*](http://www.jboss.org/reststar) - effort.) +- Lightweight interoperability. The HTTP protocol is strong enough to + be our message exchange protocol. Since interactions are RESTful the + HTTP uniform interface provides all the interoperability you need to + communicate between different languages, platforms, and even + messaging implementations that choose to implement the same RESTful + interface as Apache ActiveMQ Artemis (i.e. the [REST-\*](http://www.jboss.org/reststar) + effort.) -- No envelope (e.g. SOAP) or feed (e.g. Atom) format requirements. You - shouldn't have to learn, use, or parse a specific XML document - format in order to send and receive messages through Apache ActiveMQ Artemis's REST - interface. +- No envelope (e.g. SOAP) or feed (e.g. Atom) format requirements. You + shouldn't have to learn, use, or parse a specific XML document + format in order to send and receive messages through Apache ActiveMQ Artemis's REST + interface. -- Leverage the reliability, scalability, and clustering features of - Apache ActiveMQ Artemis on the back end without sacrificing the simplicity of a - REST interface. +- Leverage the reliability, scalability, and clustering features of + Apache ActiveMQ Artemis on the back end without sacrificing the simplicity of a + REST interface. ## Installation and Configuration @@ -63,68 +65,68 @@ Apache ActiveMQ Artemis's REST interface is installed as a Web archive (WAR). It This section should be used when you want to use the Apache ActiveMQ Artemis REST interface in an environment that already has Apache ActiveMQ Artemis installed and running. You must create a Web archive (.WAR) file with the following web.xml settings: - - - - org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap - - - - - - org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener - - - - - Rest-Messaging - - org.jboss.resteasy.plugins.server.servlet.FilterDispatcher - - - - - Rest-Messaging - /* - - +```xml + + + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + + + + org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener + + + + Rest-Messaging + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + + + + Rest-Messaging + /* + + +``` Within your WEB-INF/lib directory you must have the artemis-rest.jar file. If RESTEasy is not installed within your environment, you must add the RESTEasy jar files within the lib directory as well. Here's a sample Maven pom.xml that can build a WAR with the Apache ActiveMQ Artemis REST library. - - - 4.0.0 - org.somebody - artemis-rest - war - My App - 1.0-SNAPSHOT - - - - org.apache.activemq.rest - artemis-rest - $VERSION - - - * - * - - - - - +```xml + + + 4.0.0 + org.somebody + artemis-rest + war + My App + 1.0-SNAPSHOT + + + + org.apache.activemq.rest + artemis-rest + $VERSION + + + * + * + + + + + +``` The project structure should look this like: - |-- pom.xml - `-- src - `-- main - `-- webapp - `-- WEB-INF - `-- web.xml +``` +|-- pom.xml +`-- src + `-- main + `-- webapp + `-- WEB-INF + `-- web.xml +``` It is worth noting that when deploying a WAR in a Java EE application server like Wildfly the URL for the resulting application will include the name of the WAR by default. For example, if you've constructed a WAR as described above named "activemq-rest.war" then clients will access it at, e.g. http://localhost:8080/activemq-rest/[queues|topics]. We'll see more about this later. @@ -133,70 +135,68 @@ It is worth noting that when deploying a WAR in a Java EE application server lik You can bootstrap Apache ActiveMQ Artemis within your WAR as well. To do this, you must have the Apache ActiveMQ Artemis core and JMS jars along with Netty, RESTEasy, and the Apache ActiveMQ Artemis REST jar within your WEB-INF/lib. You must also have a broker.xml config file within WEB-INF/classes. The examples that come with the Apache ActiveMQ Artemis REST distribution show how to do this. You must also add an additional listener to your web.xml file. Here's an example: - - - - org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap - - - - - - org.apache.activemq.artemis.rest.integration.ActiveMQBootstrapListener - - - - - - org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener - - - - - Rest-Messaging - - org.jboss.resteasy.plugins.server.servlet.FilterDispatcher - - - - - Rest-Messaging - /* - - +```xml + + + org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap + + + + org.apache.activemq.artemis.rest.integration.ActiveMQBootstrapListener + + + + org.apache.activemq.artemis.rest.integration.RestMessagingBootstrapListener + + + + Rest-Messaging + org.jboss.resteasy.plugins.server.servlet.FilterDispatcher + + + + Rest-Messaging + /* + + +``` Here's a Maven pom.xml file for creating a WAR for this environment. Make sure your Apache ActiveMQ Artemis configuration file(s) are within the src/main/resources directory so that they are stuffed within the WAR's WEB-INF/classes directory! - - - 4.0.0 - org.somebody - artemis-rest - war - My App - 1.0-SNAPSHOT - - - - org.apache.activemq.rest - artemis-rest - $VERSION - - - +```xml + + + 4.0.0 + org.somebody + artemis-rest + war + My App + 1.0-SNAPSHOT + + + + org.apache.activemq.rest + artemis-rest + $VERSION + + + +``` The project structure should look this like: - |-- pom.xml - `-- src - `-- main - `-- resources - `-- broker.xml - `-- webapp - `-- WEB-INF - `-- web.xml +``` +|-- pom.xml +`-- src + `-- main + `-- resources + `-- broker.xml + `-- webapp + `-- WEB-INF + `-- web.xml +``` ### REST Configuration @@ -207,72 +207,74 @@ WEB-INF/classes directory. You must set the web.xml context-param file. Below is the format of the XML configuration file and the default values for each. - - 0 - false - false - true - topic-push-store - queue-push-store - 0 - 10 - 1 - 300 - -1 - vm://0 - +```xml + + 0 + false + false + true + topic-push-store + queue-push-store + 0 + 10 + 1 + 300 + -1 + vm://0 + +``` Let's give an explanation of each config option. -- `server-in-vm-id`. The Apache ActiveMQ Artemis REST implementation was formerly hard-coded - to use the in-vm transport to communicate with the embedded Apache ActiveMQ Artemis instance. - This is the id of the embedded instance. It is "0" by default. Note: this is deprecated in - favor of `url` which can be used to connect to an arbitrary instance of Apache ActiveMQ - Artemis (including one over the network). +- `server-in-vm-id`. The Apache ActiveMQ Artemis REST implementation was formerly hard-coded + to use the in-vm transport to communicate with the embedded Apache ActiveMQ Artemis instance. + This is the id of the embedded instance. It is "0" by default. **Note:** this is deprecated in + favor of `url` which can be used to connect to an arbitrary instance of Apache ActiveMQ + Artemis (including one over the network). -- `use-link-headers`. By default, all links (URLs) are published using - custom headers. You can instead have the Apache ActiveMQ Artemis REST - implementation publish links using the [Link Header - specification](https://tools.ietf.org/html/draft-nottingham-http-link-header-10) - instead if you desire. +- `use-link-headers`. By default, all links (URLs) are published using + custom headers. You can instead have the Apache ActiveMQ Artemis REST + implementation publish links using the [Link Header + specification](https://tools.ietf.org/html/draft-nottingham-http-link-header-10) + instead if you desire. -- `default-durable-send`. Whether a posted message should be persisted - by default if the user does not specify a durable query parameter. +- `default-durable-send`. Whether a posted message should be persisted + by default if the user does not specify a durable query parameter. -- `dups-ok`. If this is true, no duplicate detection protocol will be - enforced for message posting. +- `dups-ok`. If this is true, no duplicate detection protocol will be + enforced for message posting. -- `topic-push-store-dir`. This must be a relative or absolute file - system path. This is a directory where push registrations for topics - are stored. See [Pushing Messages](#message-push). +- `topic-push-store-dir`. This must be a relative or absolute file + system path. This is a directory where push registrations for topics + are stored. See [Pushing Messages](#message-push). -- `queue-push-store-dir`. This must be a relative or absolute file - system path. This is a directory where push registrations for queues - are stored. See [Pushing Messages](#message-push). +- `queue-push-store-dir`. This must be a relative or absolute file + system path. This is a directory where push registrations for queues + are stored. See [Pushing Messages](#message-push). -- `producer-session-pool-size`. The REST implementation pools Apache ActiveMQ Artemis - sessions for sending messages. This is the size of the pool. That - number of sessions will be created at startup time. +- `producer-session-pool-size`. The REST implementation pools Apache ActiveMQ Artemis + sessions for sending messages. This is the size of the pool. That + number of sessions will be created at startup time. -- `producer-time-to-live`. Default time to live for posted messages. - Default is no ttl. +- `producer-time-to-live`. Default time to live for posted messages. + Default is no ttl. -- `session-timeout-task-interval`. Pull consumers and pull - subscriptions can time out. This is the interval the thread that - checks for timed-out sessions will run at. A value of 1 means it - will run every 1 second. +- `session-timeout-task-interval`. Pull consumers and pull + subscriptions can time out. This is the interval the thread that + checks for timed-out sessions will run at. A value of 1 means it + will run every 1 second. -- `consumer-session-timeout-seconds`. Timeout in seconds for pull - consumers/subscriptions that remain idle for that amount of time. +- `consumer-session-timeout-seconds`. Timeout in seconds for pull + consumers/subscriptions that remain idle for that amount of time. -- `consumer-window-size`. For consumers, this config option is the - same as the Apache ActiveMQ Artemis one of the same name. It will be used by - sessions created by the Apache ActiveMQ Artemis REST implementation. - This is deprecated in favor of `url` as it can be specified as a URL - parameter. +- `consumer-window-size`. For consumers, this config option is the + same as the Apache ActiveMQ Artemis one of the same name. It will be used by + sessions created by the Apache ActiveMQ Artemis REST implementation. + This is deprecated in favor of `url` as it can be specified as a URL + parameter. -- `url`. The URL the Apache ActiveMQ Artemis REST implementation should use - to connect to the Apache ActiveMQ Artemis instance. Default to "vm://0". +- `url`. The URL the Apache ActiveMQ Artemis REST implementation should use + to connect to the Apache ActiveMQ Artemis instance. Default to "vm://0". ## Apache ActiveMQ Artemis REST Interface Basics @@ -288,8 +290,10 @@ in published XML representations. Let's look at how this works. To interact with a queue or topic you do a HEAD or GET request on the following relative URI pattern: - /queues/{name} - /topics/{name} +``` +/queues/{name} +/topics/{name} +``` The base of the URI is the base URL of the WAR you deployed the Apache ActiveMQ Artemis REST server within as defined in the [Installation and @@ -298,22 +302,24 @@ string within the above URI pattern with the name of the queue or topic you are interested in interacting with. Next, perform your HEAD or GET request on this URI. Here's what a request/response would look like. - HEAD /queues/bar HTTP/1.1 - Host: example.com - - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers +``` +HEAD /queues/bar HTTP/1.1 +Host: example.com + +--- Response --- +HTTP/1.1 200 Ok +msg-create: http://example.com/queues/bar/create +msg-create-with-id: http://example.com/queues/bar/create/{id} +msg-pull-consumers: http://example.com/queues/bar/pull-consumers +msg-push-consumers: http://example.com/queues/bar/push-consumers +``` -> **Note** +> **Note:** > > You can use the "curl" utility to test this easily. Simply execute a > command like this: > -> curl --head http://example.com/queues/bar +> curl --head http://example.com/queues/bar The HEAD or GET response contains a number of custom response headers that are URLs to additional REST resources that allow you to interact @@ -329,40 +335,40 @@ changes as the Apache ActiveMQ Artemis REST interface evolves over time. Below is a list of response headers you should expect when interacting with a Queue resource. -- `msg-create`. This is a URL you POST messages to. The semantics of - this link are described in [Posting Messages](#posting-messages). +- `msg-create`. This is a URL you POST messages to. The semantics of + this link are described in [Posting Messages](#posting-messages). -- `msg-create-with-id`. This is a URL *template* you can use to POST - messages. The semantics of this link are described in [Posting - Messages](#posting-messages). +- `msg-create-with-id`. This is a URL *template* you can use to POST + messages. The semantics of this link are described in [Posting + Messages](#posting-messages). -- `msg-pull-consumers`. This is a URL for creating consumers that will - pull from a queue. The semantics of this link are described in - [Consuming Messages via Pull](#consuming-messages-via-pull). +- `msg-pull-consumers`. This is a URL for creating consumers that will + pull from a queue. The semantics of this link are described in + [Consuming Messages via Pull](#consuming-messages-via-pull). -- `msg-push-consumers`. This is a URL for registering other URLs you - want the Apache ActiveMQ Artemis REST server to push messages to. The semantics of - this link are described in [Pushing Messages](#pushing-messages). +- `msg-push-consumers`. This is a URL for registering other URLs you + want the Apache ActiveMQ Artemis REST server to push messages to. The semantics of + this link are described in [Pushing Messages](#pushing-messages). ### Topic Resource Response Headers Below is a list of response headers you should expect when interacting with a Topic resource. -- `msg-create`. This is a URL you POST messages to. The semantics of - this link are described in [Posting Messages](#posting-messages). +- `msg-create`. This is a URL you POST messages to. The semantics of + this link are described in [Posting Messages](#posting-messages). -- `msg-create-with-id`. This is a URL *template* you can use to POST - messages. The semantics of this link are described in [Posting - Messages](#posting-messages). +- `msg-create-with-id`. This is a URL *template* you can use to POST + messages. The semantics of this link are described in [Posting + Messages](#posting-messages). -- `msg-pull-subscriptions`. This is a URL for creating subscribers - that will pull from a topic. The semantics of this link are - described in [Consuming Messages via Pull](#consuming-messages-via-pull). +- `msg-pull-subscriptions`. This is a URL for creating subscribers + that will pull from a topic. The semantics of this link are + described in [Consuming Messages via Pull](#consuming-messages-via-pull). -- `msg-push-subscriptions`. This is a URL for registering other URLs - you want the Apache ActiveMQ Artemis REST server to push messages to. The semantics - of this link are described in [Pushing Messages](#pushing-messages). +- `msg-push-subscriptions`. This is a URL for registering other URLs + you want the Apache ActiveMQ Artemis REST server to push messages to. The semantics + of this link are described in [Pushing Messages](#pushing-messages). ## Posting Messages @@ -375,7 +381,7 @@ a simple HTTP message to the URL published by the `msg-create` header. The HTTP message contains whatever content you want to publish to the Apache ActiveMQ Artemis destination. Here's an example scenario: -> **Note** +> **Note:** > > You can also post messages to the URL template found in > `msg-create-with-id`, but this is a more advanced use-case involving @@ -384,31 +390,35 @@ Apache ActiveMQ Artemis destination. Here's an example scenario: 1. Obtain the starting `msg-create` header from the queue or topic resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-create-with-id: http://example.com/queues/bar/create/{id} + ``` 2. Do a POST to the URL contained in the `msg-create` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Bill - iPhone4
- $199.99 - + + Bill + iPhone4
+ $199.99 + - --- Response --- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create + --- Response --- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create + ``` - > **Note** + > **Note:** > > You can use the "curl" utility to test this easily. Simply execute > a command like this: @@ -422,19 +432,21 @@ Apache ActiveMQ Artemis destination. Here's an example scenario: 3. POST your next message to the queue using the URL returned in the `msg-create-next` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Monica - iPad - $499.99 - + + Monica + iPad + $499.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create + ``` Continue using the new `msg-create-next` header returned with each response. @@ -467,29 +479,33 @@ discussed earlier. Here's an example: 1. Obtain the starting `msg-create` header from the queue or topic resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-create-with-id: http://example.com/queues/bar/create/{id} + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-create-with-id: http://example.com/queues/bar/create/{id} + ``` 2. Do a POST to the URL contained in the `msg-create` header. - POST /queues/bar/create - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create + Host: example.com + Content-Type: application/xml - - Bill - iPhone4 - $199.99 - + + Bill + iPhone4 + $199.99 + - --- Response --- - HTTP/1.1 307 Redirect - Location: http://example.com/queues/bar/create/13582001787372 + --- Response --- + HTTP/1.1 307 Redirect + Location: http://example.com/queues/bar/create/13582001787372 + ``` A successful response will return a 307 response code. This is standard HTTP protocol. It is telling you that you must re-POST to @@ -498,19 +514,21 @@ discussed earlier. Here's an example: 3. re-POST your message to the URL provided within the `Location` header. - POST /queues/bar/create/13582001787372 - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create/13582001787372 + Host: example.com + Content-Type: application/xml - - Bill - iPhone4 - $199.99 - + + Bill + iPhone4 + $199.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create/13582001787373 + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create/13582001787373 + ``` You should receive a 201 Created response. If there is a network failure, just re-POST to the Location header. For new messages, use @@ -518,19 +536,21 @@ discussed earlier. Here's an example: 4. POST any new message to the returned `msg-create-next` header. - POST /queues/bar/create/13582001787373 - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/create/13582001787373 + Host: example.com + Content-Type: application/xml - - Monica - iPad - $499.99 - + + Monica + iPad + $499.99 + - --- Response -- - HTTP/1.1 201 Created - msg-create-next: http://example.com/queues/bar/create/13582001787374 + --- Response -- + HTTP/1.1 201 Created + msg-create-next: http://example.com/queues/bar/create/13582001787374 + ``` If there ever is a network problem, just repost to the URL provided in the `msg-create-next` header. @@ -546,8 +566,10 @@ posted to the system. If you happen to use the same ID more than once you'll see a message like this on the server: - WARN [org.apache.activemq.artemis.core.server] (Thread-3 (Apache ActiveMQ Artemis-remoting-threads-ActiveMQServerImpl::serverUUID=8d6be6f8-5e8b-11e2-80db-51bbde66f473-26319292-267207)) AMQ112098: Duplicate message detected - message will not be routed. Message information: - ServerMessage[messageID=20,priority=4, bodySize=1500,expiration=0, durable=true, address=bar,properties=TypedProperties[{http_content$type=application/x-www-form-urlencoded, http_content$length=3, postedAsHttpMessage=true, _AMQ_DUPL_ID=42}]]@12835058 +``` +WARN [org.apache.activemq.artemis.core.server] (Thread-3 (Apache ActiveMQ Artemis-remoting-threads-ActiveMQServerImpl::serverUUID=8d6be6f8-5e8b-11e2-80db-51bbde66f473-26319292-267207)) AMQ112098: Duplicate message detected - message will not be routed. Message information: +ServerMessage[messageID=20,priority=4, bodySize=1500,expiration=0, durable=true, address=bar,properties=TypedProperties[{http_content$type=application/x-www-form-urlencoded, http_content$length=3, postedAsHttpMessage=true, _AMQ_DUPL_ID=42}]]@12835058 +``` An alternative to this approach is to use the `msg-create-with-id` header. This is not an invokable URL, but a URL template. The idea is @@ -555,7 +577,9 @@ that the client provides the `DUPLICATE_DETECTION_ID` and creates its own `create-next` URL. The `msg-create-with-id` header looks like this (you've see it in previous examples, but we haven't used it): - msg-create-with-id: http://example.com/queues/bar/create/{id} +``` +msg-create-with-id: http://example.com/queues/bar/create/{id} +``` You see that it is a regular URL appended with a `{id}`. This `{id}` is a pattern matching substring. A client would generate its @@ -580,15 +604,17 @@ called `durable` to true when you post your messages to the URLs returned in the `msg-create`, `msg-create-with-id`, or `msg-create-next` headers. here's an example of that. - POST /queues/bar/create?durable=true - Host: example.com - Content-Type: application/xml - - - Bill - iPhone4 - $199.99 - +``` +POST /queues/bar/create?durable=true +Host: example.com +Content-Type: application/xml + + + Bill + iPhone4 + $199.99 + +``` ### TTL, Expiration and Priority @@ -600,15 +626,17 @@ time in milliseconds you want the message active. The `priority` is another query parameter with an integer value between 0 and 9 expressing the priority of the message. i.e.: - POST /queues/bar/create?expiration=30000&priority=3 - Host: example.com - Content-Type: application/xml - - - Bill - iPhone4 - $199.99 - +``` +POST /queues/bar/create?expiration=30000&priority=3 +Host: example.com +Content-Type: application/xml + + + Bill + iPhone4 + $199.99 + +``` ## Consuming Messages via Pull @@ -635,31 +663,31 @@ topic. If you want to use the acknowledgement protocol and/or create a durable subscription (topics only), then you must use the form parameters (`application/x-www-form-urlencoded`) described below. -- `autoAck`. A value of `true` or `false` can be given. This defaults - to `true` if you do not pass this parameter. +- `autoAck`. A value of `true` or `false` can be given. This defaults + to `true` if you do not pass this parameter. -- `durable`. A value of `true` or `false` can be given. This defaults - to `false` if you do not pass this parameter. Only available on - topics. This specifies whether you want a durable subscription or - not. A durable subscription persists through server restart. +- `durable`. A value of `true` or `false` can be given. This defaults + to `false` if you do not pass this parameter. Only available on + topics. This specifies whether you want a durable subscription or + not. A durable subscription persists through server restart. -- `name`. This is the name of the durable subscription. If you do not - provide this parameter, the name will be automatically generated by - the server. Only usable on topics. +- `name`. This is the name of the durable subscription. If you do not + provide this parameter, the name will be automatically generated by + the server. Only usable on topics. -- `selector`. This is an optional JMS selector string. The Apache ActiveMQ Artemis - REST interface adds HTTP headers to the JMS message for REST - produced messages. HTTP headers are prefixed with "http\_" and every - '-' character is converted to a '\$'. +- `selector`. This is an optional JMS selector string. The Apache ActiveMQ Artemis + REST interface adds HTTP headers to the JMS message for REST + produced messages. HTTP headers are prefixed with "http\_" and every + '-' character is converted to a '\$'. -- `idle-timeout`. For a topic subscription, idle time in milliseconds - in which the consumer connections will be closed if idle. +- `idle-timeout`. For a topic subscription, idle time in milliseconds + in which the consumer connections will be closed if idle. -- `delete-when-idle`. Boolean value, If true, a topic subscription - will be deleted (even if it is durable) when the idle timeout is - reached. +- `delete-when-idle`. Boolean value, If true, a topic subscription + will be deleted (even if it is durable) when the idle timeout is + reached. -> **Note** +> **Note:** > > If you have multiple pull-consumers active at the same time on the > same destination be aware that unless the `consumer-window-size` is 0 @@ -672,19 +700,19 @@ This section focuses on the auto-acknowledge protocol for consuming messages via a pull. Here's a list of the response headers and URLs you'll be interested in. -- `msg-pull-consumers`. The URL of a factory resource for creating - queue consumer resources. You will pull from these created - resources. +- `msg-pull-consumers`. The URL of a factory resource for creating + queue consumer resources. You will pull from these created + resources. -- `msg-pull-subscriptions`. The URL of a factory resource for creating - topic subscription resources. You will pull from the created - resources. +- `msg-pull-subscriptions`. The URL of a factory resource for creating + topic subscription resources. You will pull from the created + resources. -- `msg-consume-next`. The URL you will pull the next message from. - This is returned with every response. +- `msg-consume-next`. The URL you will pull the next message from. + This is returned with every response. -- `msg-consumer`. This is a URL pointing back to the consumer or - subscription resource created for the client. +- `msg-consumer`. This is a URL pointing back to the consumer or + subscription resource created for the client. #### Creating an Auto-Ack Consumer or Subscription @@ -693,25 +721,29 @@ Here is an example of creating an auto-acknowledged queue pull consumer. 1. Find the pull-consumers URL by doing a HEAD or GET request to the base queue resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next do an empty POST to the URL returned in the `msg-pull-consumers` header. - POST /queues/bar/pull-consumers HTTP/1.1 - Host: example.com + ``` + POST /queues/bar/pull-consumers HTTP/1.1 + Host: example.com - --- response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/pull-consumers/auto-ack/333 - msg-consume-next: http://example.com/queues/bar/pull-consumers/auto-ack/333/consume-next-1 + --- response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/pull-consumers/auto-ack/333 + msg-consume-next: http://example.com/queues/bar/pull-consumers/auto-ack/333/consume-next-1 + ``` The `Location` header points to the JMS consumer resource that was created on the server. It is good to remember this URL, although, as @@ -725,29 +757,33 @@ pull subscription. 1. Find the `pull-subscriptions` URL by doing a HEAD or GET request to the base topic resource - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/foo/create - msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/foo/create + msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + ``` 2. Next do a POST to the URL returned in the `msg-pull-subscriptions` header passing in a `true` value for the `durable` form parameter. - POST /topics/foo/pull-subscriptions HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /topics/foo/pull-subscriptions HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - durable=true + durable=true - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/foo/pull-subscriptions/auto-ack/222 - msg-consume-next: - http://example.com/topics/foo/pull-subscriptions/auto-ack/222/consume-next-1 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/foo/pull-subscriptions/auto-ack/222 + msg-consume-next: + http://example.com/topics/foo/pull-subscriptions/auto-ack/222/consume-next-1 + ``` The `Location` header points to the JMS subscription resource that was created on the server. It is good to remember this URL, @@ -779,16 +815,18 @@ resource. 1. Do a POST on the msg-consume-next URL that was returned with the consumer or subscription resource discussed earlier. - POST /queues/bar/pull-consumers/consume-next-1 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 - msg-consumer: http://example.com/queues/bar/pull-consumers/333 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + msg-consumer: http://example.com/queues/bar/pull-consumers/333 - ... + ... + ``` The POST returns the message consumed from the queue. It also returns a new msg-consume-next link. Use this new link to get the @@ -801,13 +839,15 @@ resource. returns a new msg-consume-next link. Use this new link to get the next message. - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-2 + Host: example.com - --- Response --- - Http/1.1 503 Service Unavailable - Retry-After: 5 - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + --- Response --- + Http/1.1 503 Service Unavailable + Retry-After: 5 + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-2 + ``` In this case, there are no messages in the queue, so we get a 503 response back. As per the HTTP 1.1 spec, a 503 response may return a @@ -821,15 +861,17 @@ resource. 3. POST to the URL within the last `msg-consume-next` to get the next message. - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-2 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 - ... + ... + ``` #### Recovering From Network Failures @@ -874,21 +916,21 @@ you have received the message and that the server can internally ack the message. Here is a list of the response headers you will be interested in. -- `msg-pull-consumers`. The URL of a factory resource for creating - queue consumer resources. You will pull from these created resources +- `msg-pull-consumers`. The URL of a factory resource for creating + queue consumer resources. You will pull from these created resources -- `msg-pull-subscriptions`. The URL of a factory resource for creating - topic subscription resources. You will pull from the created - resources. +- `msg-pull-subscriptions`. The URL of a factory resource for creating + topic subscription resources. You will pull from the created + resources. -- `msg-acknowledge-next`. URL used to obtain the next message in the - queue or topic subscription. It does not acknowledge the message - though. +- `msg-acknowledge-next`. URL used to obtain the next message in the + queue or topic subscription. It does not acknowledge the message + though. -- `msg-acknowledgement`. URL used to acknowledge a message. +- `msg-acknowledgement`. URL used to acknowledge a message. -- `msg-consumer`. This is a URL pointing back to the consumer or - subscription resource created for the client. +- `msg-consumer`. This is a URL pointing back to the consumer or + subscription resource created for the client. #### Creating manually-acknowledged consumers or subscriptions @@ -897,28 +939,32 @@ Here is an example of creating an auto-acknowledged queue pull consumer. 1. Find the pull-consumers URL by doing a HEAD or GET request to the base queue resource. - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next do a POST to the URL returned in the `msg-pull-consumers` header passing in a `false` value to the `autoAck` form parameter . - POST /queues/bar/pull-consumers HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /queues/bar/pull-consumers HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - autoAck=false + autoAck=false - --- response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/pull-consumers/acknowledged/333 - msg-acknowledge-next: http://example.com/queues/bar/pull-consumers/acknowledged/333/acknowledge-next-1 + --- response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/pull-consumers/acknowledged/333 + msg-acknowledge-next: http://example.com/queues/bar/pull-consumers/acknowledged/333/acknowledge-next-1 + ``` The `Location` header points to the JMS consumer resource that was created on the server. It is good to remember this URL, although, as @@ -932,30 +978,34 @@ topic pull subscription. 1. Find the `pull-subscriptions` URL by doing a HEAD or GET request to the base topic resource - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/foo/create - msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/foo/create + msg-pull-subscriptions: http://example.com/topics/foo/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/foo/push-subscriptions + ``` 2. Next do a POST to the URL returned in the `msg-pull-subscriptions` header passing in a `true` value for the `durable` form parameter and a `false` value to the `autoAck` form parameter. - POST /topics/foo/pull-subscriptions HTTP/1.1 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /topics/foo/pull-subscriptions HTTP/1.1 + Host: example.com + Content-Type: application/x-www-form-urlencoded - durable=true&autoAck=false + durable=true&autoAck=false - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/foo/pull-subscriptions/acknowledged/222 - msg-acknowledge-next: - http://example.com/topics/foo/pull-subscriptions/acknowledged/222/consume-next-1 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/foo/pull-subscriptions/acknowledged/222 + msg-acknowledge-next: + http://example.com/topics/foo/pull-subscriptions/acknowledged/222/consume-next-1 + ``` The `Location` header points to the JMS subscription resource that was created on the server. It is good to remember this URL, @@ -982,17 +1032,19 @@ resource. 1. Do a POST on the msg-acknowledge-next URL that was returned with the consumer or subscription resource discussed earlier. - POST /queues/bar/pull-consumers/consume-next-1 - Host: example.com + ``` + POST /queues/bar/pull-consumers/consume-next-1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-acknowledgement: - http://example.com/queues/bar/pull-consumers/333/acknowledgement/2 - msg-consumer: http://example.com/queues/bar/pull-consumers/333 + --- Response --- + HTTP/1.1 200 Ok + Content-Type: application/xml + msg-acknowledgement: + http://example.com/queues/bar/pull-consumers/333/acknowledgement/2 + msg-consumer: http://example.com/queues/bar/pull-consumers/333 - ... + ... + ``` The POST returns the message consumed from the queue. It also returns a`msg-acknowledgemen`t link. You will use this new link to @@ -1007,16 +1059,18 @@ resource. whether you want to acknowledge or unacknowledge the message on the server. - POST /queues/bar/pull-consumers/acknowledgement/2 - Host: example.com - Content-Type: application/x-www-form-urlencoded + ``` + POST /queues/bar/pull-consumers/acknowledgement/2 + Host: example.com + Content-Type: application/x-www-form-urlencoded - acknowledge=true + acknowledge=true - --- Response --- - Http/1.1 204 Ok - msg-acknowledge-next: - http://example.com/queues/bar/pull-consumers/333/acknowledge-next-2 + --- Response --- + Http/1.1 204 Ok + msg-acknowledge-next: + http://example.com/queues/bar/pull-consumers/333/acknowledge-next-2 + ``` Whether you acknowledge or unacknowledge the message, the response will contain a new msg-acknowledge-next header that you must use to @@ -1075,16 +1129,18 @@ response from the server. The value of this header is the time in seconds the client is willing to block for. You would send this request header with your pull requests. Here's an example: - POST /queues/bar/pull-consumers/consume-next-2 - Host: example.com - Accept-Wait: 30 +``` +POST /queues/bar/pull-consumers/consume-next-2 +Host: example.com +Accept-Wait: 30 - --- Response --- - HTTP/1.1 200 Ok - Content-Type: application/xml - msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 +--- Response --- +HTTP/1.1 200 Ok +Content-Type: application/xml +msg-consume-next: http://example.com/queues/bar/pull-consumers/333/consume-next-3 - ... +... +``` In this example, we're posting to a msg-consume-next URL and telling the server that we would be willing to block for 30 seconds. @@ -1117,17 +1173,19 @@ provide a URL to ship the forwarded message to. Finally, you have to provide authentication information if the final endpoint requires authentication. Here's a simple example: - - false - 1 - ]]> - - - 5 - 1000 - true - +```xml + + false + 1 + ]]> + + + 5 + 1000 + true + +``` The `durable` element specifies whether the registration should be saved to disk so that if there is a server restart, the push subscription will @@ -1162,36 +1220,42 @@ it is not provided it defaults to POST. The `rel` attribute is very important and the value of it triggers different behavior. Here's the values a rel attribute can have: -- `destination`. The href URL is assumed to be a queue or topic - resource of another Apache ActiveMQ Artemis REST server. The push registration will - initially do a HEAD request to this URL to obtain a - msg-create-with-id header. It will use this header to push new - messages to the Apache ActiveMQ Artemis REST endpoint reliably. Here's an example: - - - - - -- `template`. In this case, the server is expecting the link element's - href attribute to be a URL expression. The URL expression must have - one and only one URL parameter within it. The server will use a - unique value to create the endpoint URL. Here's an example: - - - - - - In this example, the {id} sub-string is the one and only one URL - parameter. - -- `user defined`. If the rel attributes is not destination or template - (or is empty or missing), then the server will send an HTTP message - to the href URL using the HTTP method defined in the method - attribute. Here's an example: - - - - +- `destination`. The href URL is assumed to be a queue or topic + resource of another Apache ActiveMQ Artemis REST server. The push registration will + initially do a HEAD request to this URL to obtain a + msg-create-with-id header. It will use this header to push new + messages to the Apache ActiveMQ Artemis REST endpoint reliably. Here's an example: + + ```xml + + + + ``` + +- `template`. In this case, the server is expecting the link element's + href attribute to be a URL expression. The URL expression must have + one and only one URL parameter within it. The server will use a + unique value to create the endpoint URL. Here's an example: + + ``` + + + + ``` + + In this example, the {id} sub-string is the one and only one URL + parameter. + +- `user defined`. If the rel attributes is not destination or template + (or is empty or missing), then the server will send an HTTP message + to the href URL using the HTTP method defined in the method + attribute. Here's an example: + + ``` + + + + ``` ### The Topic Push Subscription XML @@ -1200,14 +1264,16 @@ push-topic-registration. (Also remember the `selector` element is optional). The rest of the document is the same. Here's an example of a template registration: - - true - 1 - ]]> - - - +```xml + + true + 1 + ]]> + + + +``` ### Creating a Push Subscription at Runtime @@ -1218,29 +1284,33 @@ Here's an example of creating a push registration for a queue: 1. First do a HEAD request to the queue resource: - HEAD /queues/bar HTTP/1.1 - Host: example.com + ``` + HEAD /queues/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/queues/bar/create - msg-pull-consumers: http://example.com/queues/bar/pull-consumers - msg-push-consumers: http://example.com/queues/bar/push-consumers + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/queues/bar/create + msg-pull-consumers: http://example.com/queues/bar/pull-consumers + msg-push-consumers: http://example.com/queues/bar/push-consumers + ``` 2. Next POST your subscription XML to the URL returned from msg-push-consumers header - POST /queues/bar/push-consumers - Host: example.com - Content-Type: application/xml + ``` + POST /queues/bar/push-consumers + Host: example.com + Content-Type: application/xml - - - + + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/bar/push-consumers/1-333-1212 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/queues/bar/push-consumers/1-333-1212 + ``` The Location header contains the URL for the created resource. If you want to unregister this, then do a HTTP DELETE on this URL. @@ -1249,29 +1319,33 @@ Here's an example of creating a push registration for a topic: 1. First do a HEAD request to the topic resource: - HEAD /topics/bar HTTP/1.1 - Host: example.com + ``` + HEAD /topics/bar HTTP/1.1 + Host: example.com - --- Response --- - HTTP/1.1 200 Ok - msg-create: http://example.com/topics/bar/create - msg-pull-subscriptions: http://example.com/topics/bar/pull-subscriptions - msg-push-subscriptions: http://example.com/topics/bar/push-subscriptions + --- Response --- + HTTP/1.1 200 Ok + msg-create: http://example.com/topics/bar/create + msg-pull-subscriptions: http://example.com/topics/bar/pull-subscriptions + msg-push-subscriptions: http://example.com/topics/bar/push-subscriptions + ``` 2. Next POST your subscription XML to the URL returned from msg-push-subscriptions header - POST /topics/bar/push-subscriptions - Host: example.com - Content-Type: application/xml + ``` + POST /topics/bar/push-subscriptions + Host: example.com + Content-Type: application/xml - - - + + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/bar/push-subscriptions/1-333-1212 + --- Response --- + HTTP/1.1 201 Created + Location: http://example.com/topics/bar/push-subscriptions/1-333-1212 + ``` The Location header contains the URL for the created resource. If you want to unregister this, then do a HTTP DELETE on this URL. @@ -1291,38 +1365,44 @@ Here's an example of a hand-created queue registration. This file must go in the directory specified by the queue-push-store-dir config variable defined in Chapter 2: - - bar - true - - +```xml + + bar + true + + +``` Here's an example of a hand-created topic registration. This file must go in the directory specified by the topic-push-store-dir config variable defined in Chapter 2: - - my-subscription-1true - - foo - +```xml + + my-subscription-1true + + foo + +``` ### Pushing to Authenticated Servers Push subscriptions only support BASIC and DIGEST authentication out of the box. Here is an example of adding BASIC authentication: - - true - - - - guest - geheim - - - +```xml + + true + + + + guest + geheim + + + +``` For DIGEST, just replace basic-auth with digest-auth. @@ -1331,11 +1411,13 @@ transmitted with each request. Use the header element with the name attribute representing the name of the header. Here's what custom headers might look like: - - true - -
jfdiwe3321
- +```xml + + true + +
jfdiwe3321
+
+``` ## Creating Destinations @@ -1344,32 +1426,36 @@ Currently you cannot create a temporary queue or topic. To create a queue you do a POST to the relative URL /queues with an XML representation of the queue. For example: - POST /queues - Host: example.com - Content-Type: application/activemq.xml +``` +POST /queues +Host: example.com +Content-Type: application/activemq.xml - - true - + + true + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/queues/testQueue +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/queues/testQueue +``` Notice that the Content-Type is application/activemq.xml. Here's what creating a topic would look like: - POST /topics - Host: example.com - Content-Type: application/activemq.xml +``` +POST /topics +Host: example.com +Content-Type: application/activemq.xml - - + + - --- Response --- - HTTP/1.1 201 Created - Location: http://example.com/topics/testTopic +--- Response --- +HTTP/1.1 201 Created +Location: http://example.com/topics/testTopic +``` ## Securing the Apache ActiveMQ Artemis REST Interface @@ -1389,16 +1475,16 @@ web.xml for every path of every queue and topic you have deployed. Here is a list of URI patterns: Post | Description - --- | --- - /queues | secure the POST operation to secure queue creation - /queues/{queue-name}/create/ | secure this URL pattern for producing messages. - /queues/{queue-name}/pull-consumers/ | secure this URL pattern for pushing messages. - /queues/{queue-name}/push-consumers/ | secure the POST operation to secure topic creation - /topics | secure the POST operation to secure topic creation - /topics/{topic-name} | secure the GET HEAD operation to getting information about the topic. - /topics/{topic-name}/create/ | secure this URL pattern for producing messages - /topics/{topic-name}/pull-subscriptions/ | secure this URL pattern for pulling messages - /topics/{topic-name}/push-subscriptions/ | secure this URL pattern for pushing messages +--- | --- +/queues | secure the POST operation to secure queue creation +/queues/{queue-name}/create/ | secure this URL pattern for producing messages. +/queues/{queue-name}/pull-consumers/ | secure this URL pattern for pushing messages. +/queues/{queue-name}/push-consumers/ | secure the POST operation to secure topic creation +/topics | secure the POST operation to secure topic creation +/topics/{topic-name} | secure the GET HEAD operation to getting information about the topic. +/topics/{topic-name}/create/ | secure this URL pattern for producing messages +/topics/{topic-name}/pull-subscriptions/ | secure this URL pattern for pulling messages +/topics/{topic-name}/push-subscriptions/ | secure this URL pattern for pushing messages ## Mixing JMS and REST @@ -1419,8 +1505,10 @@ server will use RESTEasy content handlers (MessageBodyReader/Writers) to transform the Java object to the type desired. Here's an example of a JMS producer setting the content type of the message. - ObjectMessage message = session.createObjectMessage(); - message.setStringProperty(org.apache.activemq.rest.HttpHeaderProperty.CONTENT_TYPE, "application/xml"); +```java +ObjectMessage message = session.createObjectMessage(); +message.setStringProperty(org.apache.activemq.rest.HttpHeaderProperty.CONTENT_TYPE, "application/xml"); +``` If the JMS producer does not set the content-type, then this information must be obtained from the REST consumer. If it is a pull consumer, then @@ -1436,8 +1524,7 @@ Apache ActiveMQ Artemis REST has a simple helper class for you to transform the body to a Java object. Here's some example code: ```java -public void onMessage(Message message) -{ +public void onMessage(Message message) { MyType obj = org.apache.activemq.rest.Jms.getEntity(message, MyType.class); } ``` diff --git a/docs/user-manual/en/scheduled-messages.md b/docs/user-manual/en/scheduled-messages.md index 6dff58a0256..9d1f07f31bc 100644 --- a/docs/user-manual/en/scheduled-messages.md +++ b/docs/user-manual/en/scheduled-messages.md @@ -30,5 +30,5 @@ same property on the core message before sending. ## Example -See the [examples](examples.md) chapter for an example which shows how scheduled messages can be used with +See the [Scheduled Message Example](examples.md#scheduled-message) which shows how scheduled messages can be used with JMS. diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index 2f707014936..dfd1a41b2b0 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -1,606 +1,755 @@ # Security -This chapter describes how security works with Apache ActiveMQ Artemis and how you can -configure it. To disable security completely simply set the -`security-enabled` property to false in the `broker.xml` -file. - -For performance reasons security is cached and invalidated every so -long. To change this period set the property -`security-invalidation-interval`, which is in milliseconds. The default -is `10000` ms. - -To assist in security auditing the `populate-validated-user` option exists. If this is `true` then -the server will add the name of the validated user to the message using the key `_AMQ_VALIDATED_USER`. -For JMS and Stomp clients this is mapped to the key `JMSXUserID`. For users authenticated based on -their SSL certificate this name is the name to which their certificate's DN maps. If `security-enabled` -is `false` and `populate-validated-user` is `true` then the server will simply use whatever user name -(if any) the client provides. This option is `false` by default. +This chapter describes how security works with Apache ActiveMQ Artemis and how +you can configure it. To disable security completely simply set the +`security-enabled` property to false in the `broker.xml` file. + +For performance reasons security is cached and invalidated every so long. To +change this period set the property `security-invalidation-interval`, which is +in milliseconds. The default is `10000` ms. + +## Tracking the Validated User + +To assist in security auditing the `populate-validated-user` option exists. If +this is `true` then the server will add the name of the validated user to the +message using the key `_AMQ_VALIDATED_USER`. For JMS and Stomp clients this is +mapped to the key `JMSXUserID`. For users authenticated based on their SSL +certificate this name is the name to which their certificate's DN maps. If +`security-enabled` is `false` and `populate-validated-user` is `true` then the +server will simply use whatever user name (if any) the client provides. This +option is `false` by default. ## Role based security for addresses -Apache ActiveMQ Artemis contains a flexible role-based security model for applying -security to queues, based on their addresses. +Apache ActiveMQ Artemis contains a flexible role-based security model for +applying security to queues, based on their addresses. -As explained in [Using Core](using-core.md), Apache ActiveMQ Artemis core consists mainly of sets of queues bound -to addresses. A message is sent to an address and the server looks up -the set of queues that are bound to that address, the server then routes -the message to those set of queues. +As explained in [Using Core](core.md), Apache ActiveMQ Artemis core consists +mainly of sets of queues bound to addresses. A message is sent to an address +and the server looks up the set of queues that are bound to that address, the +server then routes the message to those set of queues. -Apache ActiveMQ Artemis allows sets of permissions to be defined against the queues -based on their address. An exact match on the address can be used or a -wildcard match can be used using the wildcard characters '`#`' and -'`*`'. +Apache ActiveMQ Artemis allows sets of permissions to be defined against the +queues based on their address. An exact match on the address can be used or a +[wildcard match](wildcard-syntax.md) can be used. -Eight different permissions can be given to the set of queues which -match the address. Those permissions are: +Eight different permissions can be given to the set of queues which match the +address. Those permissions are: -- `createAddress`. This permission allows the user to create an - address fitting the `match`. +- `createAddress`. This permission allows the user to create an address fitting + the `match`. -- `deleteAddress`. This permission allows the user to delete an - address fitting the `match`. +- `deleteAddress`. This permission allows the user to delete an address fitting + the `match`. -- `createDurableQueue`. This permission allows the user to create a - durable queue under matching addresses. +- `createDurableQueue`. This permission allows the user to create a durable + queue under matching addresses. -- `deleteDurableQueue`. This permission allows the user to delete a - durable queue under matching addresses. +- `deleteDurableQueue`. This permission allows the user to delete a durable + queue under matching addresses. -- `createNonDurableQueue`. This permission allows the user to create a - non-durable queue under matching addresses. +- `createNonDurableQueue`. This permission allows the user to create a + non-durable queue under matching addresses. -- `deleteNonDurableQueue`. This permission allows the user to delete a - non-durable queue under matching addresses. +- `deleteNonDurableQueue`. This permission allows the user to delete a + non-durable queue under matching addresses. -- `send`. This permission allows the user to send a message to - matching addresses. +- `send`. This permission allows the user to send a message to matching + addresses. -- `consume`. This permission allows the user to consume a message from - a queue bound to matching addresses. +- `consume`. This permission allows the user to consume a message from a queue + bound to matching addresses. -- `browse`. This permission allows the user to browse a queue bound to - the matching address. +- `browse`. This permission allows the user to browse a queue bound to the + matching address. -- `manage`. This permission allows the user to invoke management - operations by sending management messages to the management address. +- `manage`. This permission allows the user to invoke management operations by + sending management messages to the management address. For each permission, a list of roles who are granted that permission is -specified. If the user has any of those roles, he/she will be granted -that permission for that set of addresses. - -Let's take a simple example, here's a security block from -`broker.xml` file: - - - - - - - - - - -The '`#`' character signifies "any sequence of words". Words are -delimited by the '`.`' character. For a full description of the wildcard -syntax please see [Understanding the Wildcard Syntax](wildcard-syntax.md). -The above security block applies to any address -that starts with the string "globalqueues.europe.": - -Only users who have the `admin` role can create or delete durable queues -bound to an address that starts with the string "globalqueues.europe." - -Any users with the roles `admin`, `guest`, or `europe-users` can create -or delete temporary queues bound to an address that starts with the -string "globalqueues.europe." - -Any users with the roles `admin` or `europe-users` can send messages to -these addresses or consume messages from queues bound to an address that -starts with the string "globalqueues.europe." - -The mapping between a user and what roles they have is handled by the -security manager. Apache ActiveMQ Artemis ships with a user manager that reads user +specified. If the user has any of those roles, he/she will be granted that +permission for that set of addresses. + +Let's take a simple example, here's a security block from `broker.xml` file: + +```xml + + + + + + + + +``` + +Using the default [wildcard syntax](wildcard-syntax.md) the `#` character +signifies "any sequence of words". Words are delimited by the `.` character. +Therefore, the above security block applies to any address that starts with the +string "globalqueues.europe.". + +Only users who have the `admin` role can create or delete durable queues bound +to an address that starts with the string "globalqueues.europe." + +Any users with the roles `admin`, `guest`, or `europe-users` can create or +delete temporary queues bound to an address that starts with the string +"globalqueues.europe." + +Any users with the roles `admin` or `europe-users` can send messages to these +addresses or consume messages from queues bound to an address that starts with +the string "globalqueues.europe." + +The mapping between a user and what roles they have is handled by the security +manager. Apache ActiveMQ Artemis ships with a user manager that reads user credentials from a file on disk, and can also plug into JAAS or JBoss Application Server security. -For more information on configuring the security manager, please see 'Changing the Security Manager'. +For more information on configuring the security manager, please see 'Changing +the Security Manager'. -There can be zero or more `security-setting` elements in each xml file. -Where more than one match applies to a set of addresses the *more -specific* match takes precedence. +There can be zero or more `security-setting` elements in each xml file. Where +more than one match applies to a set of addresses the *more specific* match +takes precedence. -Let's look at an example of that, here's another `security-setting` -block: +Let's look at an example of that, here's another `security-setting` block: - - - - +```xml + + + + +``` -In this `security-setting` block the match -'globalqueues.europe.orders.\#' is more specific than the previous match -'globalqueues.europe.\#'. So any addresses which match -'globalqueues.europe.orders.\#' will take their security settings *only* -from the latter security-setting block. +In this `security-setting` block the match 'globalqueues.europe.orders.\#' is +more specific than the previous match 'globalqueues.europe.\#'. So any +addresses which match 'globalqueues.europe.orders.\#' will take their security +settings *only* from the latter security-setting block. -Note that settings are not inherited from the former block. All the -settings will be taken from the more specific matching block, so for the -address 'globalqueues.europe.orders.plastics' the only permissions that -exist are `send` and `consume` for the role europe-users. The -permissions `createDurableQueue`, `deleteDurableQueue`, -`createNonDurableQueue`, `deleteNonDurableQueue` are not inherited from -the other security-setting block. +Note that settings are not inherited from the former block. All the settings +will be taken from the more specific matching block, so for the address +'globalqueues.europe.orders.plastics' the only permissions that exist are +`send` and `consume` for the role europe-users. The permissions +`createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, +`deleteNonDurableQueue` are not inherited from the other security-setting +block. -By not inheriting permissions, it allows you to effectively deny -permissions in more specific security-setting blocks by simply not -specifying them. Otherwise it would not be possible to deny permissions -in sub-groups of addresses. +By not inheriting permissions, it allows you to effectively deny permissions in +more specific security-setting blocks by simply not specifying them. Otherwise +it would not be possible to deny permissions in sub-groups of addresses. ## Security Setting Plugin -Aside from configuring sets of permissions via XML these permissions can alternatively be -configured via a plugin which implements `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` e.g.: - - - - - - - - - - - +Aside from configuring sets of permissions via XML these permissions can +alternatively be configured via a plugin which implements +`org.apache.activemq.artemis.core.server.SecuritySettingPlugin` e.g.: + +```xml + + + + + + + + + + +``` -Most of this configuration is specific to the plugin implementation. However, there are two configuration details that -will be specified for every implementation: +Most of this configuration is specific to the plugin implementation. However, +there are two configuration details that will be specified for every +implementation: -- `class-name`. This attribute of `security-setting-plugin` indicates the name of the class which implements - `org.apache.activemq.artemis.core.server.SecuritySettingPlugin`. +- `class-name`. This attribute of `security-setting-plugin` indicates the name + of the class which implements + `org.apache.activemq.artemis.core.server.SecuritySettingPlugin`. -- `setting`. Each of these elements represents a name/value pair that will be passed to the implementation for configuration - purposes. +- `setting`. Each of these elements represents a name/value pair that will be + passed to the implementation for configuration purposes. -See the JavaDoc on `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` for further details about the interface -and what each method is expected to do. +See the JavaDoc on +`org.apache.activemq.artemis.core.server.SecuritySettingPlugin` for further +details about the interface and what each method is expected to do. ### Available plugins #### LegacyLDAPSecuritySettingPlugin -This plugin will read the security information that was previously handled by [`LDAPAuthorizationMap`](http://activemq.apache.org/security.html) -and the [`cachedLDAPAuthorizationMap`](http://activemq.apache.org/cached-ldap-authorization-module.html) in Apache ActiveMQ 5.x -and turn it into Artemis security settings where possible. The security implementations of ActiveMQ 5.x and Artemis don't -match perfectly so some translation must occur to achieve near equivalent functionality. +This plugin will read the security information that was previously handled by +[`LDAPAuthorizationMap`](http://activemq.apache.org/security.html) and the +[`cachedLDAPAuthorizationMap`](http://activemq.apache.org/cached-ldap-authorization-module.html) +in Apache ActiveMQ 5.x and turn it into Artemis security settings where +possible. The security implementations of ActiveMQ 5.x and Artemis don't match +perfectly so some translation must occur to achieve near equivalent +functionality. Here is an example of the plugin's configuration: - - - - - - - - - -- `class-name`. The implementation is `org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin`. - -- `initialContextFactory`. The initial context factory used to connect to LDAP. It must always be set to - `com.sun.jndi.ldap.LdapCtxFactory` (i.e. the default value). - -- `connectionURL`. Specifies the location of the directory server using an ldap URL, `ldap://Host:Port`. You can - optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory - tree. For example, `ldap://ldapserver:10389/ou=system`. The default is `ldap://localhost:1024`. - -- `connectionUsername`. The DN of the user that opens the connection to the directory server. For example, `uid=admin,ou=system`. - Directory servers generally require clients to present username/password credentials in order to open a connection. - -- `connectionPassword`. The password that matches the DN from `connectionUsername`. In the directory server, in the - DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry. - -- `connectionProtocol`. Currently the only supported value is a blank string. In future, this option will allow you to - select the Secure Socket Layer (SSL) for the connection to the directory server. Note: this option must be set - explicitly to an empty string, because it has no default value. - -- `authentication`. Specifies the authentication method used when binding to the LDAP server. Can take either of the - values, `simple` (username and password, the default value) or `none` (anonymous). Note: Simple Authentication and - Security Layer (SASL) authentication is currently not supported. - -- `destinationBase`. Specifies the DN of the node whose children provide the permissions for all destinations. In this - case the DN is a literal value (that is, no string substitution is performed on the property value). For example, a - typical value of this property is `ou=destinations,o=ActiveMQ,ou=system` (i.e. the default value). - -- `filter`. Specifies an LDAP search filter, which is used when looking up the permissions for any kind of destination. - The search filter attempts to match one of the children or descendants of the queue or topic node. The default value - is `(cn=*)`. - -- `roleAttribute`. Specifies an attribute of the node matched by `filter`, whose value is the DN of a role. Default - value is `uniqueMember`. - -- `adminPermissionValue`. Specifies a value that matches the `admin` permission. The default value is `admin`. - -- `readPermissionValue`. Specifies a value that matches the `read` permission. The default value is `read`. - -- `writePermissionValue`. Specifies a value that matches the `write` permission. The default value is `write`. - -- `enableListener`. Whether or not to enable a listener that will automatically receive updates made in the LDAP server - and update the broker's authorization configuration in real-time. The default value is `true`. - -The name of the queue or topic defined in LDAP will serve as the "match" for the security-setting, the permission value -will be mapped from the ActiveMQ 5.x type to the Artemis type, and the role will be mapped as-is. - -ActiveMQ 5.x only has 3 permission types - `read`, `write`, and `admin`. These permission types are described on their -[website](http://activemq.apache.org/security.html). However, as described previously, ActiveMQ Artemis has 9 permission -types - `createAddress`, `deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, -`deleteNonDurableQueue`, `send`, `consume`, `browse`, and `manage`. Here's how the old types are mapped to the new types: - -- `read` - `consume`, `browse` -- `write` - `send` -- `admin` - `createAddress`, `deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, - `deleteNonDurableQueue` - -As mentioned, there are a few places where a translation was performed to achieve some equivalence.: - -- This mapping doesn't include the Artemis `manage` permission type since there is no type analogous for that in ActiveMQ - 5.x. +```xml + + + + + + + + +``` -- The `admin` permission in ActiveMQ 5.x relates to whether or not the broker will auto-create a destination if - it doesn't exist and the user sends a message to it. Artemis automatically allows the automatic creation of a - destination if the user has permission to send message to it. Therefore, the plugin will map the `admin` permission - to the 4 aforementioned permissions in Artemis. +- `class-name`. The implementation is + `org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin`. + +- `initialContextFactory`. The initial context factory used to connect to LDAP. + It must always be set to `com.sun.jndi.ldap.LdapCtxFactory` (i.e. the default + value). + +- `connectionURL`. Specifies the location of the directory server using an ldap + URL, `ldap://Host:Port`. You can optionally qualify this URL, by adding a + forward slash, `/`, followed by the DN of a particular node in the directory + tree. For example, `ldap://ldapserver:10389/ou=system`. The default is + `ldap://localhost:1024`. + +- `connectionUsername`. The DN of the user that opens the connection to the + directory server. For example, `uid=admin,ou=system`. Directory servers + generally require clients to present username/password credentials in order to + open a connection. + +- `connectionPassword`. The password that matches the DN from + `connectionUsername`. In the directory server, in the DIT, the password is + normally stored as a `userPassword` attribute in the corresponding directory + entry. + +- `connectionProtocol`. Currently the only supported value is a blank string. + In future, this option will allow you to select the Secure Socket Layer (SSL) +for the connection to the directory server. **Note:** this option must be set +explicitly to an empty string, because it has no default value. + +- `authentication`. Specifies the authentication method used when binding to + the LDAP server. Can take either of the values, `simple` (username and +password, the default value) or `none` (anonymous). **Note:** Simple Authentication +and Security Layer (SASL) authentication is currently not supported. + +- `destinationBase`. Specifies the DN of the node whose children provide the + permissions for all destinations. In this case the DN is a literal value + (that is, no string substitution is performed on the property value). For + example, a typical value of this property is + `ou=destinations,o=ActiveMQ,ou=system` (i.e. the default value). + +- `filter`. Specifies an LDAP search filter, which is used when looking up the + permissions for any kind of destination. The search filter attempts to match + one of the children or descendants of the queue or topic node. The default + value is `(cn=*)`. + +- `roleAttribute`. Specifies an attribute of the node matched by `filter`, + whose value is the DN of a role. Default value is `uniqueMember`. + +- `adminPermissionValue`. Specifies a value that matches the `admin` + permission. The default value is `admin`. + +- `readPermissionValue`. Specifies a value that matches the `read` permission. + The default value is `read`. + +- `writePermissionValue`. Specifies a value that matches the `write` + permission. The default value is `write`. + +- `enableListener`. Whether or not to enable a listener that will automatically + receive updates made in the LDAP server and update the broker's authorization + configuration in real-time. The default value is `true`. + +The name of the queue or topic defined in LDAP will serve as the "match" for +the security-setting, the permission value will be mapped from the ActiveMQ 5.x +type to the Artemis type, and the role will be mapped as-is. + +ActiveMQ 5.x only has 3 permission types - `read`, `write`, and `admin`. These +permission types are described on their +[website](http://activemq.apache.org/security.html). However, as described +previously, ActiveMQ Artemis has 9 permission types - `createAddress`, +`deleteAddress`, `createDurableQueue`, `deleteDurableQueue`, +`createNonDurableQueue`, `deleteNonDurableQueue`, `send`, `consume`, `browse`, +and `manage`. Here's how the old types are mapped to the new types: + +- `read` - `consume`, `browse` +- `write` - `send` +- `admin` - `createAddress`, `deleteAddress`, `createDurableQueue`, + `deleteDurableQueue`, `createNonDurableQueue`, `deleteNonDurableQueue` + +As mentioned, there are a few places where a translation was performed to +achieve some equivalence.: + +- This mapping doesn't include the Artemis `manage` permission type since there + is no type analogous for that in ActiveMQ 5.x. + +- The `admin` permission in ActiveMQ 5.x relates to whether or not the broker + will auto-create a destination if it doesn't exist and the user sends a + message to it. Artemis automatically allows the automatic creation of a + destination if the user has permission to send message to it. Therefore, the + plugin will map the `admin` permission to the 4 aforementioned permissions in + Artemis. ## Secure Sockets Layer (SSL) Transport -When messaging clients are connected to servers, or servers are connected to other servers (e.g. via bridges) over an -untrusted network then Apache ActiveMQ Artemis allows that traffic to be encrypted using the Secure Sockets Layer (SSL) -transport. +When messaging clients are connected to servers, or servers are connected to +other servers (e.g. via bridges) over an untrusted network then Apache ActiveMQ +Artemis allows that traffic to be encrypted using the Secure Sockets Layer +(SSL) transport. -For more information on configuring the SSL transport, please see [Configuring the Transport](configuring-transports.md). +For more information on configuring the SSL transport, please see [Configuring +the Transport](configuring-transports.md). ## User credentials Apache ActiveMQ Artemis ships with two security manager implementations: -- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, i.e. user names, passwords and role -information from properties files on the classpath called `artemis-users.properties` and `artemis-roles.properties`. +- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials, + i.e. user names, passwords and role information from properties files on the + classpath called `artemis-users.properties` and `artemis-roles.properties`. -- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any standard JAAS login module. Artemis ships -with several login modules which will be discussed further down. This is the default security manager. +- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any + standard JAAS login module. Artemis ships with several login modules which + will be discussed further down. This is the default security manager. ### JAAS Security Manager -When using the Java Authentication and Authorization Service (JAAS) much of the configuration depends on which login -module is used. However, there are a few commonalities for every case. -The first place to look is in `bootstrap.xml`. Here is an example using the `PropertiesLogin` JAAS login -module which reads user, password, and role information from properties files: +When using the Java Authentication and Authorization Service (JAAS) much of the +configuration depends on which login module is used. However, there are a few +commonalities for every case. The first place to look is in `bootstrap.xml`. +Here is an example using the `PropertiesLogin` JAAS login module which reads +user, password, and role information from properties files: - +```xml + +``` -No matter what login module you're using, you'll need to specify it here in `bootstrap.xml`. The `domain` attribute -here refers to the relevant login module entry in `login.config`. For example: +No matter what login module you're using, you'll need to specify it here in +`bootstrap.xml`. The `domain` attribute here refers to the relevant login +module entry in `login.config`. For example: - PropertiesLogin { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; +``` +PropertiesLogin { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; +``` -The `login.config` file is a standard JAAS configuration file. You can read more about this file on -[Oracle's website](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html). +The `login.config` file is a standard JAAS configuration file. You can read +more about this file on [Oracle's +website](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html). In short, the file defines: -- an alias for an entry (e.g. `PropertiesLogin`) +- an alias for an entry (e.g. `PropertiesLogin`) -- the implementation class for the login module (e.g. `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`) +- the implementation class for the login module (e.g. + `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`) -- a flag which indicates whether the success of the login module is `required`, `requisite`, `sufficient`, or `optional` -(see more details on these flags in the [JavaDoc](https://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html) +- a flag which indicates whether the success of the login module is `required`, + `requisite`, `sufficient`, or `optional` (see more details on these flags in + the + [JavaDoc](https://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html) -- a list of configuration options specific to the login module implementation +- a list of configuration options specific to the login module implementation -By default, the location and name of `login.config` is specified on the Artemis command-line which is set by -`etc/artemis.profile` on linux and `etc\artemis.profile.cmd` on Windows. +By default, the location and name of `login.config` is specified on the Artemis +command-line which is set by `etc/artemis.profile` on linux and +`etc\artemis.profile.cmd` on Windows. #### Dual Authentication -The JAAS Security Manager also supports another configuration parameter - `certificate-domain`. This is useful when you -want to authenticate clients connecting with SSL connections based on their SSL certificates (e.g. using the `CertificateLoginModule` -discussed below) but you still want to authenticate clients connecting with non-SSL connections with, e.g., username and -password. Here's an example of what would go in `bootstrap.xml`: +The JAAS Security Manager also supports another configuration parameter - +`certificate-domain`. This is useful when you want to authenticate clients +connecting with SSL connections based on their SSL certificates (e.g. using the +`CertificateLoginModule` discussed below) but you still want to authenticate +clients connecting with non-SSL connections with, e.g., username and password. +Here's an example of what would go in `bootstrap.xml`: - +```xml + +``` And here's the corresponding `login.config`: - PropertiesLogin { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required - debug=false - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; - - CertLogin { - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required - debug=true - org.apache.activemq.jaas.textfiledn.user="cert-users.properties" - org.apache.activemq.jaas.textfiledn.role="cert-roles.properties"; - }; +``` +PropertiesLogin { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required + debug=false + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; + +CertLogin { + org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required + debug=true + org.apache.activemq.jaas.textfiledn.user="cert-users.properties" + org.apache.activemq.jaas.textfiledn.role="cert-roles.properties"; +}; +``` -When the broker is configured this way then any client connecting with SSL and a client certificate will be authenticated -using `CertLogin` and any client connecting without SSL will be authenticated using `PropertiesLogin`. +When the broker is configured this way then any client connecting with SSL and +a client certificate will be authenticated using `CertLogin` and any client +connecting without SSL will be authenticated using `PropertiesLogin`. ### JAAS Login Modules #### GuestLoginModule -Allows users without credentials (and, depending on how it is configured, possibly also users with invalid credentials) -to access the broker. Normally, the guest login module is chained with another login module, such as a properties login -module. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule`. -- `org.apache.activemq.jaas.guest.user` - the user name to assign; default is "guest" +Allows users without credentials (and, depending on how it is configured, +possibly also users with invalid credentials) to access the broker. Normally, +the guest login module is chained with another login module, such as a +properties login module. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule`. -- `org.apache.activemq.jaas.guest.role` - the role name to assign; default is "guests" +- `org.apache.activemq.jaas.guest.user` - the user name to assign; default is "guest" -- `credentialsInvalidate` - boolean flag; if `true`, reject login requests that include a password (i.e. guest login -succeeds only when the user does not provide a password); default is `false` +- `org.apache.activemq.jaas.guest.role` - the role name to assign; default is "guests" -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it -should be set to `false`, or omitted; default is `false` +- `credentialsInvalidate` - boolean flag; if `true`, reject login requests that + include a password (i.e. guest login succeeds only when the user does not + provide a password); default is `false` + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` There are two basic use cases for the guest login module, as follows: -- Guests with no credentials or invalid credentials. +- Guests with no credentials or invalid credentials. -- Guests with no credentials only. +- Guests with no credentials only. -The following snippet shows how to configure a JAAS login entry for the use case where users with no credentials or -invalid credentials are logged in as guests. In this example, the guest login module is used in combination with the +The following snippet shows how to configure a JAAS login entry for the use +case where users with no credentials or invalid credentials are logged in as +guests. In this example, the guest login module is used in combination with the properties login module. - activemq-domain { - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - - org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient - debug=true - org.apache.activemq.jaas.guest.user="anyone" - org.apache.activemq.jaas.guest.role="restricted"; - }; +``` +activemq-domain { + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule sufficient + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; + + org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient + debug=true + org.apache.activemq.jaas.guest.user="anyone" + org.apache.activemq.jaas.guest.role="restricted"; +}; +``` Depending on the user login data, authentication proceeds as follows: -- User logs in with a valid password — the properties login module successfully authenticates the user and returns - immediately. The guest login module is not invoked. - -- User logs in with an invalid password — the properties login module fails to authenticate the user, and authentication - proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal. +- User logs in with a valid password — the properties login module successfully + authenticates the user and returns immediately. The guest login module is not + invoked. -- User logs in with a blank password — the properties login module fails to authenticate the user, and authentication - proceeds to the guest login module. The guest login module successfully authenticates the user and returns the guest principal. +- User logs in with an invalid password — the properties login module fails to + authenticate the user, and authentication proceeds to the guest login module. + The guest login module successfully authenticates the user and returns the + guest principal. -The following snipped shows how to configure a JAAS login entry for the use case where only those users with no -credentials are logged in as guests. To support this use case, you must set the credentialsInvalidate option to true in -the configuration of the guest login module. You should also note that, compared with the preceding example, the order -of the login modules is reversed and the flag attached to the properties login module is changed to requisite. +- User logs in with a blank password — the properties login module fails to + authenticate the user, and authentication proceeds to the guest login module. + The guest login module successfully authenticates the user and returns the + guest principal. - activemq-guest-when-no-creds-only-domain { - org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient - debug=true - credentialsInvalidate=true - org.apache.activemq.jaas.guest.user="guest" - org.apache.activemq.jaas.guest.role="guests"; +The following snipped shows how to configure a JAAS login entry for the use +case where only those users with no credentials are logged in as guests. To +support this use case, you must set the credentialsInvalidate option to true in +the configuration of the guest login module. You should also note that, +compared with the preceding example, the order of the login modules is reversed +and the flag attached to the properties login module is changed to requisite. - org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule requisite - debug=true - org.apache.activemq.jaas.properties.user="artemis-users.properties" - org.apache.activemq.jaas.properties.role="artemis-roles.properties"; - }; +``` +activemq-guest-when-no-creds-only-domain { + org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule sufficient + debug=true + credentialsInvalidate=true + org.apache.activemq.jaas.guest.user="guest" + org.apache.activemq.jaas.guest.role="guests"; + + org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule requisite + debug=true + org.apache.activemq.jaas.properties.user="artemis-users.properties" + org.apache.activemq.jaas.properties.role="artemis-roles.properties"; +}; +``` Depending on the user login data, authentication proceeds as follows: -- User logs in with a valid password — the guest login module fails to authenticate the user (because the user has - presented a password while the credentialsInvalidate option is enabled) and authentication proceeds to the properties - login module. The properties login module successfully authenticates the user and returns. +- User logs in with a valid password — the guest login module fails to + authenticate the user (because the user has presented a password while the + credentialsInvalidate option is enabled) and authentication proceeds to the + properties login module. The properties login module successfully authenticates + the user and returns. -- User logs in with an invalid password — the guest login module fails to authenticate the user and authentication proceeds - to the properties login module. The properties login module also fails to authenticate the user. The nett result is - authentication failure. +- User logs in with an invalid password — the guest login module fails to + authenticate the user and authentication proceeds to the properties login + module. The properties login module also fails to authenticate the user. The + net result is authentication failure. -- User logs in with a blank password — the guest login module successfully authenticates the user and returns immediately. - The properties login module is not invoked. +- User logs in with a blank password — the guest login module successfully + authenticates the user and returns immediately. The properties login module + is not invoked. #### PropertiesLoginModule -The JAAS properties login module provides a simple store of authentication data, where the relevant user data is stored -in a pair of flat files. This is convenient for demonstrations and testing, but for an enterprise system, the integration -with LDAP is preferable. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`. - -- `org.apache.activemq.jaas.properties.user` - the path to the file which contains user and password properties - -- `org.apache.activemq.jaas.properties.role` - the path to the file which contains user and role properties - -- `reload` - boolean flag; whether or not to reload the properties files when a modification occurs; default is `false` - -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it -should be set to `false`, or omitted; default is `false` - -In the context of the properties login module, the `artemis-users.properties` file consists of a list of properties of the -form, `UserName=Password`. For example, to define the users `system`, `user`, and `guest`, you could create a file like -the following: - - system=manager - user=password - guest=password - -Passwords in `artemis-users.properties` can be hashed. Such passwords should follow the syntax `ENC()`. Hashed -passwords can easily be added to `artemis-users.properties` using the `user` CLI command, e.g.: +The JAAS properties login module provides a simple store of authentication +data, where the relevant user data is stored in a pair of flat files. This is +convenient for demonstrations and testing, but for an enterprise system, the +integration with LDAP is preferable. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule`. + +- `org.apache.activemq.jaas.properties.user` - the path to the file which + contains user and password properties + +- `org.apache.activemq.jaas.properties.role` - the path to the file which + contains user and role properties + +- `reload` - boolean flag; whether or not to reload the properties files when a + modification occurs; default is `false` + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` + +In the context of the properties login module, the `artemis-users.properties` +file consists of a list of properties of the form, `UserName=Password`. For +example, to define the users `system`, `user`, and `guest`, you could create a +file like the following: + +```properties +system=manager +user=password +guest=password +``` +Passwords in `artemis-users.properties` can be hashed. Such passwords should +follow the syntax `ENC()`. Hashed passwords can easily be added to +`artemis-users.properties` using the `user` CLI command, e.g.: ```sh - ./artemis user add --username guest --password guest --role admin +./artemis user add --username guest --password guest --role admin ``` -This will use the default `org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a "one-way" hash of -the password and alter both the `artemis-users.properties` and `artemis-roles.properties` files with the specified values. +This will use the default +`org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec` to perform a +"one-way" hash of the password and alter both the `artemis-users.properties` +and `artemis-roles.properties` files with the specified values. -The `artemis-roles.properties` file consists of a list of properties of the form, `Role=UserList`, where UserList is a -comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file -like the following: +The `artemis-roles.properties` file consists of a list of properties of the +form, `Role=UserList`, where UserList is a comma-separated list of users. For +example, to define the roles `admins`, `users`, and `guests`, you could create +a file like the following: - admins=system - users=system,user - guests=guest +```properties +admins=system +users=system,user +guests=guest +``` #### LDAPLoginModule -The LDAP login module enables you to perform authentication and authorization by checking the incoming credentials against -user data stored in a central X.500 directory server. For systems that already have an X.500 directory server in place, -this means that you can rapidly integrate ActiveMQ Artemis with the existing security database and user accounts can be -managed using the X.500 system. It is implemented by `org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule`. - -- `initialContextFactory` - must always be set to `com.sun.jndi.ldap.LdapCtxFactory` - -- `connectionURL` - specify the location of the directory server using an ldap URL, ldap://Host:Port. You can - optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory - tree. For example, ldap://ldapserver:10389/ou=system. -- `authentication` - specifies the authentication method used when binding to the LDAP server. Can take either of - the values, `simple` (username and password), `GSSAPI` (Kerberos SASL) or `none` (anonymous). - -- `connectionUsername` - the DN of the user that opens the connection to the directory server. For example, - `uid=admin,ou=system`. Directory servers generally require clients to present username/password credentials in order - to open a connection. - -- `connectionPassword` - the password that matches the DN from `connectionUsername`. In the directory server, - in the DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry. - -- `saslLoginConfigScope` - the scope in JAAS configuration (login.config) to use to obtain Kerberos initiator credentials - when the `authentication` method is SASL `GSSAPI`. The default value is `broker-sasl-gssapi`. - -- `connectionProtocol` - currently, the only supported value is a blank string. In future, this option will allow - you to select the Secure Socket Layer (SSL) for the connection to the directory server. This option must be set - explicitly to an empty string, because it has no default value. - -- `userBase` - selects a particular subtree of the DIT to search for user entries. The subtree is specified by a - DN, which specifes the base node of the subtree. For example, by setting this option to `ou=User,ou=ActiveMQ,ou=system`, - the search for user entries is restricted to the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node. - -- `userSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `userBase`. - Before passing to the LDAP search operation, the string value you provide here is subjected to string substitution, - as implemented by the `java.text.MessageFormat` class. Essentially, this means that the special string, `{0}`, is - substituted by the username, as extracted from the incoming client credentials. - - After substitution, the string is interpreted as an LDAP search filter, where the LDAP search filter syntax is - defined by the IETF standard, RFC 2254. A short introduction to the search filter syntax is available from Oracle's - JNDI tutorial, [Search Filters](https://docs.oracle.com/javase/jndi/tutorial/basics/directory/filter.html). - - For example, if this option is set to `(uid={0})` and the received username is `jdoe`, the search filter becomes - `(uid=jdoe)` after string substitution. If the resulting search filter is applied to the subtree selected by the - user base, `ou=User,ou=ActiveMQ,ou=system`, it would match the entry, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system` - (and possibly more deeply nested entries, depending on the specified search depth—see the `userSearchSubtree` option). - -- `userSearchSubtree` - specify the search depth for user entries, relative to the node specified by `userBase`. - This option is a boolean. `false` indicates it will try to match one of the child entries of the `userBase` node - (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` indicates it will try to match any entry - belonging to the subtree of the `userBase` node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). - -- `userRoleName` - specifies the name of the multi-valued attribute of the user entry that contains a list of - role names for the user (where the role names are interpreted as group names by the broker's authorization plug-in). - If you omit this option, no role names are extracted from the user entry. - -- `roleBase` - if you want to store role data directly in the directory server, you can use a combination of role - options (`roleBase`, `roleSearchMatching`, `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition - to) specifying the `userRoleName` option. This option selects a particular subtree of the DIT to search for role/group - entries. The subtree is specified by a DN, which specifes the base node of the subtree. For example, by setting this - option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries is restricted to the subtree beneath - the `ou=Group,ou=ActiveMQ,ou=system` node. - -- `roleName` - specifies the attribute type of the role entry that contains the name of the role/group (e.g. C, O, - OU, etc.). If you omit this option the full DN of the role is used. - -- `roleSearchMatching` - specifies an LDAP search filter, which is applied to the subtree selected by `roleBase`. - This works in a similar manner to the `userSearchMatching` option, except that it supports two substitution strings, - as follows: - - - `{0}` - substitutes the full DN of the matched user entry (that is, the result of the user search). For - example, for the user, `jdoe`, the substituted string could be `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`. - - - `{1}` - substitutes the received username. For example, `jdoe`. - - For example, if this option is set to `(member=uid={1})` and the received username is `jdoe`, the search filter - becomes `(member=uid=jdoe)` after string substitution (assuming ApacheDS search filter syntax). If the resulting - search filter is applied to the subtree selected by the role base, `ou=Group,ou=ActiveMQ,ou=system`, it matches all - role entries that have a `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a DN). - - This option must always be set to enable role searching because it has no default value. Leaving it unset disables - role searching and the role information must come from `userRoleName`. - - If you use OpenLDAP, the syntax of the search filter is `(member:=uid=jdoe)`. - -- `roleSearchSubtree` - specify the search depth for role entries, relative to the node specified by `roleBase`. - This option can take boolean values, as follows: - - - `false` (default) - try to match one of the child entries of the roleBase node (maps to - `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). - - - `true` — try to match any entry belonging to the subtree of the roleBase node (maps to - `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). - -- `authenticateUser` - boolean flag to disable authentication. Useful as an optimisation when this module is used just for - role mapping of a Subject's existing authenticated principals; default is `false`. - -- `referral` - specify how to handle referrals; valid values: `ignore`, `follow`, `throw`; default is `ignore`. - -- `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it - should be set to `false`, or omitted; default is `false` - -Add user entries under the node specified by the `userBase` option. When creating a new user entry in the directory, -choose an object class that supports the `userPassword` attribute (for example, the `person` or `inetOrgPerson` object -classes are typically suitable). After creating the user entry, add the `userPassword` attribute, to hold the user's -password. - -If you want to store role data in dedicated role entries (where each node represents a particular role), create a role -entry as follows. Create a new child of the `roleBase` node, where the `objectClass` of the child is `groupOfNames`. Set -the `cn` (or whatever attribute type is specified by `roleName`) of the new child node equal to the name of the -role/group. Define a `member` attribute for each member of the role/group, setting the `member` value to the DN of the -corresponding user (where the DN is specified either fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially, -`uid=jdoe`). - -If you want to add roles to user entries, you would need to customize the directory schema, by adding a suitable -attribute type to the user entry's object class. The chosen attribute type must be capable of handling multiple values. +The LDAP login module enables you to perform authentication and authorization +by checking the incoming credentials against user data stored in a central +X.500 directory server. For systems that already have an X.500 directory server +in place, this means that you can rapidly integrate ActiveMQ Artemis with the +existing security database and user accounts can be managed using the X.500 +system. It is implemented by +`org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule`. + +- `initialContextFactory` - must always be set to + `com.sun.jndi.ldap.LdapCtxFactory` + +- `connectionURL` - specify the location of the directory server using an ldap + URL, ldap://Host:Port. You can optionally qualify this URL, by adding a + forward slash, `/`, followed by the DN of a particular node in the directory + tree. For example, ldap://ldapserver:10389/ou=system. + +- `authentication` - specifies the authentication method used when binding to + the LDAP server. Can take either of the values, `simple` (username and + password), `GSSAPI` (Kerberos SASL) or `none` (anonymous). + +- `connectionUsername` - the DN of the user that opens the connection to the + directory server. For example, `uid=admin,ou=system`. Directory servers + generally require clients to present username/password credentials in order to + open a connection. + +- `connectionPassword` - the password that matches the DN from + `connectionUsername`. In the directory server, in the DIT, the password is + normally stored as a `userPassword` attribute in the corresponding directory + entry. + +- `saslLoginConfigScope` - the scope in JAAS configuration (login.config) to + use to obtain Kerberos initiator credentials when the `authentication` method + is SASL `GSSAPI`. The default value is `broker-sasl-gssapi`. + +- `connectionProtocol` - currently, the only supported value is a blank string. + In future, this option will allow you to select the Secure Socket Layer (SSL) + for the connection to the directory server. This option must be set explicitly + to an empty string, because it has no default value. + +- `userBase` - selects a particular subtree of the DIT to search for user + entries. The subtree is specified by a DN, which specifes the base node of + the subtree. For example, by setting this option to + `ou=User,ou=ActiveMQ,ou=system`, the search for user entries is restricted to + the subtree beneath the `ou=User,ou=ActiveMQ,ou=system` node. + +- `userSearchMatching` - specifies an LDAP search filter, which is applied to + the subtree selected by `userBase`. Before passing to the LDAP search + operation, the string value you provide here is subjected to string + substitution, as implemented by the `java.text.MessageFormat` class. + Essentially, this means that the special string, `{0}`, is substituted by the + username, as extracted from the incoming client credentials. + + After substitution, the string is interpreted as an LDAP search filter, + where the LDAP search filter syntax is defined by the IETF standard, RFC 2254. + A short introduction to the search filter syntax is available from Oracle's + JNDI tutorial, [Search + Filters](https://docs.oracle.com/javase/jndi/tutorial/basics/directory/filter.html). + + For example, if this option is set to `(uid={0})` and the received username + is `jdoe`, the search filter becomes `(uid=jdoe)` after string substitution. If + the resulting search filter is applied to the subtree selected by the user + base, `ou=User,ou=ActiveMQ,ou=system`, it would match the entry, + `uid=jdoe,ou=User,ou=ActiveMQ,ou=system` (and possibly more deeply nested + entries, depending on the specified search depth—see the `userSearchSubtree` + option). + +- `userSearchSubtree` - specify the search depth for user entries, relative to + the node specified by `userBase`. This option is a boolean. `false` + indicates it will try to match one of the child entries of the `userBase` node + (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). `true` + indicates it will try to match any entry belonging to the subtree of the + `userBase` node (maps to + `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). + +- `userRoleName` - specifies the name of the multi-valued attribute of the user + entry that contains a list of role names for the user (where the role names + are interpreted as group names by the broker's authorization plug-in). If you + omit this option, no role names are extracted from the user entry. + +- `roleBase` - if you want to store role data directly in the directory server, + you can use a combination of role options (`roleBase`, `roleSearchMatching`, + `roleSearchSubtree`, and `roleName`) as an alternative to (or in addition to) + specifying the `userRoleName` option. This option selects a particular subtree + of the DIT to search for role/group entries. The subtree is specified by a DN, + which specifes the base node of the subtree. For example, by setting this + option to `ou=Group,ou=ActiveMQ,ou=system`, the search for role/group entries + is restricted to the subtree beneath the `ou=Group,ou=ActiveMQ,ou=system` node. + +- `roleName` - specifies the attribute type of the role entry that contains the + name of the role/group (e.g. C, O, OU, etc.). If you omit this option the + full DN of the role is used. + +- `roleSearchMatching` - specifies an LDAP search filter, which is applied to + the subtree selected by `roleBase`. This works in a similar manner to the + `userSearchMatching` option, except that it supports two substitution strings, + as follows: + + - `{0}` - substitutes the full DN of the matched user entry (that is, the + result of the user search). For example, for the user, `jdoe`, the + substituted string could be `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`. + + - `{1}` - substitutes the received username. For example, `jdoe`. + + For example, if this option is set to `(member=uid={1})` and the received + username is `jdoe`, the search filter becomes `(member=uid=jdoe)` after string + substitution (assuming ApacheDS search filter syntax). If the resulting search + filter is applied to the subtree selected by the role base, + `ou=Group,ou=ActiveMQ,ou=system`, it matches all role entries that have a + `member` attribute equal to `uid=jdoe` (the value of a `member` attribute is a + DN). + + This option must always be set to enable role searching because it has no + default value. Leaving it unset disables role searching and the role + information must come from `userRoleName`. + + If you use OpenLDAP, the syntax of the search filter is + `(member:=uid=jdoe)`. + +- `roleSearchSubtree` - specify the search depth for role entries, relative to + the node specified by `roleBase`. This option can take boolean values, as + follows: + + - `false` (default) - try to match one of the child entries of the roleBase + node (maps to `javax.naming.directory.SearchControls.ONELEVEL_SCOPE`). + + - `true` — try to match any entry belonging to the subtree of the roleBase + node (maps to `javax.naming.directory.SearchControls.SUBTREE_SCOPE`). + +- `authenticateUser` - boolean flag to disable authentication. Useful as an + optimisation when this module is used just for role mapping of a Subject's + existing authenticated principals; default is `false`. + +- `referral` - specify how to handle referrals; valid values: `ignore`, + `follow`, `throw`; default is `ignore`. + +- `debug` - boolean flag; if `true`, enable debugging; this is used only for + testing or debugging; normally, it should be set to `false`, or omitted; + default is `false` + +Add user entries under the node specified by the `userBase` option. When +creating a new user entry in the directory, choose an object class that +supports the `userPassword` attribute (for example, the `person` or +`inetOrgPerson` object classes are typically suitable). After creating the user +entry, add the `userPassword` attribute, to hold the user's password. + +If you want to store role data in dedicated role entries (where each node +represents a particular role), create a role entry as follows. Create a new +child of the `roleBase` node, where the `objectClass` of the child is +`groupOfNames`. Set the `cn` (or whatever attribute type is specified by +`roleName`) of the new child node equal to the name of the role/group. Define a +`member` attribute for each member of the role/group, setting the `member` +value to the DN of the corresponding user (where the DN is specified either +fully, `uid=jdoe,ou=User,ou=ActiveMQ,ou=system`, or partially, `uid=jdoe`). + +If you want to add roles to user entries, you would need to customize the +directory schema, by adding a suitable attribute type to the user entry's +object class. The chosen attribute type must be capable of handling multiple +values. #### CertificateLoginModule -The JAAS certificate authentication login module must be used in combination with SSL and the clients must be configured -with their own certificate. In this scenario, authentication is actually performed during the SSL/TLS handshake, not -directly by the JAAS certificate authentication plug-in. The role of the plug-in is as follows: +The JAAS certificate authentication login module must be used in combination +with SSL and the clients must be configured with their own certificate. In this +scenario, authentication is actually performed during the SSL/TLS handshake, +not directly by the JAAS certificate authentication plug-in. The role of the +plug-in is as follows: -- To further constrain the set of acceptable users, because only the user DNs explicitly listed in the relevant - properties file are eligible to be authenticated. +- To further constrain the set of acceptable users, because only the user DNs + explicitly listed in the relevant properties file are eligible to be + authenticated. -- To associate a list of groups with the received user identity, facilitating integration with the authorization feature. +- To associate a list of groups with the received user identity, facilitating + integration with the authorization feature. -- To require the presence of an incoming certificate (by default, the SSL/TLS layer is configured to treat the - presence of a client certificate as optional). +- To require the presence of an incoming certificate (by default, the SSL/TLS + layer is configured to treat the presence of a client certificate as + optional). -The JAAS certificate login module stores a collection of certificate DNs in a pair of flat files. The files associate a -username and a list of group IDs with each DN. +The JAAS certificate login module stores a collection of certificate DNs in a +pair of flat files. The files associate a username and a list of group IDs with +each DN. The certificate login module is implemented by the following class: - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule +```java +org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule +``` -The following `CertLogin` login entry shows how to configure certificate login module in the login.config file: +The following `CertLogin` login entry shows how to configure certificate login +module in the login.config file: - CertLogin { - org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule - debug=true - org.apache.activemq.jaas.textfiledn.user="users.properties" - org.apache.activemq.jaas.textfiledn.role="roles.properties"; - }; +``` +CertLogin { + org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule + debug=true + org.apache.activemq.jaas.textfiledn.user="users.properties" + org.apache.activemq.jaas.textfiledn.role="roles.properties"; +}; +``` -In the preceding example, the JAAS realm is configured to use a single `org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule` +In the preceding example, the JAAS realm is configured to use a single +`org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule` login module. The options supported by this login module are as follows: - `debug` - boolean flag; if true, enable debugging; this is used only for testing or debugging; normally, @@ -632,116 +781,183 @@ compares it with the subject DNs in the `users.properties` file by testing for s be careful to ensure that the subject DNs appearing in the `users.properties` file are an exact match for the subject DNs extracted from the user certificates. -Note: Technically, there is some residual ambiguity in the DN string format. For example, the `domainComponent` attribute -could be represented in a string either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. Normally, you do -not need to worry about this ambiguity. But it could potentially be a problem, if you changed the underlying -implementation of the Java security layer. - -The easiest way to obtain the subject DNs from the user certificates is by invoking the `keytool` utility to print the -certificate contents. To print the contents of a certificate in a keystore, perform the following steps: +- `org.apache.activemq.jaas.textfiledn.user` - specifies the location of the + user properties file (relative to the directory containing the login + configuration file). -1. Export the certificate from the keystore file into a temporary file. For example, to export the certificate with - alias `broker-localhost` from the `broker.ks` keystore file, enter the following command: +- `org.apache.activemq.jaas.textfiledn.role` - specifies the location of the + role properties file (relative to the directory containing the login + configuration file). - keytool -export -file broker.export -alias broker-localhost -keystore broker.ks -storepass password +- `reload` - boolean flag; whether or not to reload the properties files when a + modification occurs; default is `false` - After running this command, the exported certificate is in the file, `broker.export`. +In the context of the certificate login module, the `users.properties` file +consists of a list of properties of the form, `UserName=StringifiedSubjectDN`. +For example, to define the users, system, user, and guest, you could create a +file like the following: -1. Print out the contents of the exported certificate. For example, to print out the contents of `broker.export`, - enter the following command: - - keytool -printcert -file broker.export - - Which should produce output similar to that shown here: - - Owner: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown - Issuer: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown - Serial number: 4537c82e - Valid from: Thu Oct 19 19:47:10 BST 2006 until: Wed Jan 17 18:47:10 GMT 2007 - Certificate fingerprints: - MD5: 3F:6C:0C:89:A8:80:29:CC:F5:2D:DA:5C:D7:3F:AB:37 - SHA1: F0:79:0D:04:38:5A:46:CE:86:E1:8A:20:1F:7B:AB:3A:46:E4:34:5C - - The string following `Owner:` gives the subject DN. The format used to enter the subject DN depends on your - platform. The `Owner:` string above could be represented as either `CN=localhost,\ OU=broker,\ O=Unknown,\ L=Unknown,\ ST=Unknown,\ C=Unknown` - or `CN=localhost,OU=broker,O=Unknown,L=Unknown,ST=Unknown,C=Unknown`. - -The `roles.properties` file consists of a list of properties of the form, `Role=UserList`, where `UserList` is a -comma-separated list of users. For example, to define the roles `admins`, `users`, and `guests`, you could create a file -like the following: +```properties +system=CN=system,O=Progress,C=US +user=CN=humble user,O=Progress,C=US +guest=CN=anon,O=Progress,C=DE +``` - admins=system - users=system,user - guests=guest +Each username is mapped to a subject DN, encoded as a string (where the string +encoding is specified by RFC 2253). For example, the system username is mapped +to the `CN=system,O=Progress,C=US` subject DN. When performing authentication, +the plug-in extracts the subject DN from the received certificate, converts it +to the standard string format, and compares it with the subject DNs in the +`users.properties` file by testing for string equality. Consequently, you must +be careful to ensure that the subject DNs appearing in the `users.properties` +file are an exact match for the subject DNs extracted from the user +certificates. + +**Note:** Technically, there is some residual ambiguity in the DN string format. +For example, the `domainComponent` attribute could be represented in a string +either as the string, `DC`, or as the OID, `0.9.2342.19200300.100.1.25`. +Normally, you do not need to worry about this ambiguity. But it could +potentially be a problem, if you changed the underlying implementation of the +Java security layer. + +The easiest way to obtain the subject DNs from the user certificates is by +invoking the `keytool` utility to print the certificate contents. To print the +contents of a certificate in a keystore, perform the following steps: + +1. Export the certificate from the keystore file into a temporary file. For + example, to export the certificate with alias `broker-localhost` from the +`broker.ks` keystore file, enter the following command: + + ```sh + keytool -export -file broker.export -alias broker-localhost -keystore broker.ks -storepass password + ``` + + After running this command, the exported certificate is in the file, + `broker.export`. + +1. Print out the contents of the exported certificate. For example, to print + out the contents of `broker.export`, enter the following command: + + ```sh + keytool -printcert -file broker.export + ``` + + Which should produce output similar to that shown here: + + ``` + Owner: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown + Issuer: CN=localhost, OU=broker, O=Unknown, L=Unknown, ST=Unknown, C=Unknown + Serial number: 4537c82e + Valid from: Thu Oct 19 19:47:10 BST 2006 until: Wed Jan 17 18:47:10 GMT 2007 + Certificate fingerprints: + MD5: 3F:6C:0C:89:A8:80:29:CC:F5:2D:DA:5C:D7:3F:AB:37 + SHA1: F0:79:0D:04:38:5A:46:CE:86:E1:8A:20:1F:7B:AB:3A:46:E4:34:5C + ``` + + The string following `Owner:` gives the subject DN. The format used to enter + the subject DN depends on your platform. The `Owner:` string above could be + represented as either `CN=localhost,\ OU=broker,\ O=Unknown,\ L=Unknown,\ + ST=Unknown,\ C=Unknown` or + `CN=localhost,OU=broker,O=Unknown,L=Unknown,ST=Unknown,C=Unknown`. + +The `roles.properties` file consists of a list of properties of the form, +`Role=UserList`, where `UserList` is a comma-separated list of users. For +example, to define the roles `admins`, `users`, and `guests`, you could create +a file like the following: + +```properties +admins=system +users=system,user +guests=guest +``` -The simplest way to make the login configuration available to JAAS is to add the directory containing the file, -`login.config`, to your CLASSPATH. +The simplest way to make the login configuration available to JAAS is to add +the directory containing the file, `login.config`, to your CLASSPATH. ### Kerberos Authentication -You must have the Kerberos infrastructure set up in your deployment environment before the server can accept Kerberos credentials. -The server can acquire its Kerberos acceptor credentials by using JAAS and a Kerberos login module. The JDK provides the +You must have the Kerberos infrastructure set up in your deployment environment +before the server can accept Kerberos credentials. The server can acquire its +Kerberos acceptor credentials by using JAAS and a Kerberos login module. The +JDK provides the [Krb5LoginModule](https://docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/Krb5LoginModule.html) -which executes the necessary Kerberos protocol steps to authenticate and obtain Kerberos credentials. +which executes the necessary Kerberos protocol steps to authenticate and obtain +Kerberos credentials. #### GSSAPI SASL Mechanism -Using SASL over [AMQP](using-AMQP.md), Kerberos authentication is supported using the `GSSAPI` SASL mechanism. -With SASL doing Kerberos authentication, TLS can be used to provide integrity and confidentially to the communications +Using SASL over [AMQP](using-AMQP.md), Kerberos authentication is supported +using the `GSSAPI` SASL mechanism. With SASL doing Kerberos authentication, +TLS can be used to provide integrity and confidentially to the communications channel in the normal way. -The `GSSAPI` SASL mechanism must be enabled on the AMQP acceptor in `broker.xml` by adding it to the `saslMechanisms` list url parameter: + +The `GSSAPI` SASL mechanism must be enabled on the AMQP acceptor in +`broker.xml` by adding it to the `saslMechanisms` list url parameter: `saslMechanisms="GSSAPI<,PLAIN, etc>`. - tcp://0.0.0.0:5672?protocols=AMQP;saslMechanisms=GSSAPI +```xml +tcp://0.0.0.0:5672?protocols=AMQP;saslMechanisms=GSSAPI +``` -The GSSAPI mechanism implementation on the server will use a JAAS configuration scope named `amqp-sasl-gssapi` to -obtain it's Kerberos acceptor credentials. An alternative configuration scope can be specified on the AMQP acceptor -using the url parameter: `saslLoginConfigScope=`. +The GSSAPI mechanism implementation on the server will use a JAAS configuration +scope named `amqp-sasl-gssapi` to obtain it's Kerberos acceptor credentials. An +alternative configuration scope can be specified on the AMQP acceptor using the +url parameter: `saslLoginConfigScope=`. -An example configuration scope for `login.config` that will pick up a Kerberos keyTab for the Kerberos acceptor Principal -`amqp/localhost` is as follows: +An example configuration scope for `login.config` that will pick up a Kerberos +keyTab for the Kerberos acceptor Principal `amqp/localhost` is as follows: - amqp-sasl-gssapi { - com.sun.security.auth.module.Krb5LoginModule required - isInitiator=false - storeKey=true - useKeyTab=true - principal="amqp/localhost" - debug=true; - }; +``` +amqp-sasl-gssapi { + com.sun.security.auth.module.Krb5LoginModule required + isInitiator=false + storeKey=true + useKeyTab=true + principal="amqp/localhost" + debug=true; +}; +``` #### Role Mapping -On the server, the Kerberos authenticated Peer Principal can be added to the Subject's principal set as an Apache ActiveMQ Artemis UserPrincipal -using the Apache ActiveMQ Artemis `Krb5LoginModule` login module. The [PropertiesLoginModule](#propertiesloginmodule) or - [LDAPLoginModule](#ldaploginmodule) can then be used to map -the authenticated Kerberos Peer Principal to an Apache ActiveMQ Artemis [Role](#role-based-security-for-addresses). Note -that the Kerberos Peer Principal does not exist as an Apache ActiveMQ Artemis user, only as a role member. - - org.apache.activemq.artemis.spi.core.security.jaas.Krb5LoginModule required - ; - org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule optional - initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory - connectionURL="ldap://localhost:1024" - authentication=GSSAPI - saslLoginConfigScope=broker-sasl-gssapi - connectionProtocol=s - userBase="ou=users,dc=example,dc=com" - userSearchMatching="(krb5PrincipalName={0})" - userSearchSubtree=true - authenticateUser=false - roleBase="ou=system" - roleName=cn - roleSearchMatching="(member={0})" - roleSearchSubtree=false - ; +On the server, the Kerberos authenticated Peer Principal can be added to the +Subject's principal set as an Apache ActiveMQ Artemis UserPrincipal using the +Apache ActiveMQ Artemis `Krb5LoginModule` login module. The +[PropertiesLoginModule](#propertiesloginmodule) or +[LDAPLoginModule](#ldaploginmodule) can then be used to map the authenticated +Kerberos Peer Principal to an Apache ActiveMQ Artemis +[Role](#role-based-security-for-addresses). Note that the Kerberos Peer +Principal does not exist as an Apache ActiveMQ Artemis user, only as a role +member. + +``` +org.apache.activemq.artemis.spi.core.security.jaas.Krb5LoginModule required + ; +org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule optional + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + authentication=GSSAPI + saslLoginConfigScope=broker-sasl-gssapi + connectionProtocol=s + userBase="ou=users,dc=example,dc=com" + userSearchMatching="(krb5PrincipalName={0})" + userSearchSubtree=true + authenticateUser=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(member={0})" + roleSearchSubtree=false + ; +``` #### TLS Kerberos Cipher Suites -The legacy [rfc2712](https://www.ietf.org/rfc/rfc2712.txt) defines TLS Kerberos cipher suites that can be used by TLS to negotiate -Kerberos authentication. The cypher suites offered by rfc2712 are dated and insecure and rfc2712 has been superseded by -SASL GSSAPI. However, for clients that don't support SASL (core client), using TLS can provide Kerberos authentication -over an *unsecure* channel. +The legacy [rfc2712](https://www.ietf.org/rfc/rfc2712.txt) defines TLS Kerberos +cipher suites that can be used by TLS to negotiate Kerberos authentication. The +cypher suites offered by rfc2712 are dated and insecure and rfc2712 has been +superseded by SASL GSSAPI. However, for clients that don't support SASL (core +client), using TLS can provide Kerberos authentication over an *unsecure* +channel. ## SASL @@ -751,81 +967,95 @@ Note: EXTERNAL will only be chosen if a subject is available from the TLS client ## Changing the username/password for clustering -In order for cluster connections to work correctly, each node in the -cluster must make connections to the other nodes. The username/password -they use for this should always be changed from the installation default -to prevent a security risk. +In order for cluster connections to work correctly, each node in the cluster +must make connections to the other nodes. The username/password they use for +this should always be changed from the installation default to prevent a +security risk. Please see [Management](management.md) for instructions on how to do this. ## Securing the console -Artemis comes with a web console that allows user to browse Artemis documentation via an embedded server. By default the -web access is plain HTTP. It is configured in `bootstrap.xml`: +Artemis comes with a web console that allows user to browse Artemis +documentation via an embedded server. By default the web access is plain HTTP. +It is configured in `bootstrap.xml`: - - - +```xml + + + +``` -Alternatively you can edit the above configuration to enable secure access using HTTPS protocol. e.g.: +Alternatively you can edit the above configuration to enable secure access +using HTTPS protocol. e.g.: - - - +```xml + + + +``` -As shown in the example, to enable https the first thing to do is config the `bind` to be an `https` url. In addition, -You will have to configure a few extra properties desribed as below. +As shown in the example, to enable https the first thing to do is config the +`bind` to be an `https` url. In addition, You will have to configure a few +extra properties desribed as below. -- `keyStorePath` - The path of the key store file. +- `keyStorePath` - The path of the key store file. -- `keyStorePassword` - The key store's password. +- `keyStorePassword` - The key store's password. -- `clientAuth` - The boolean flag indicates whether or not client authentication is required. Default is `false`. +- `clientAuth` - The boolean flag indicates whether or not client + authentication is required. Default is `false`. -- `trustStorePath` - The path of the trust store file. This is needed only if `clientAuth` is `true`. +- `trustStorePath` - The path of the trust store file. This is needed only if + `clientAuth` is `true`. -- `trustStorePassword` - The trust store's password. +- `trustStorePassword` - The trust store's password. ## Controlling JMS ObjectMessage deserialization -Artemis provides a simple class filtering mechanism with which a user can specify which -packages are to be trusted and which are not. Objects whose classes are from trusted packages -can be deserialized without problem, whereas those from 'not trusted' packages will be denied -deserialization. - -Artemis keeps a `black list` to keep track of packages that are not trusted and a `white list` -for trusted packages. By default both lists are empty, meaning any serializable object is -allowed to be deserialized. If an object whose class matches one of the packages in black list, -it is not allowed to be deserialized. If it matches one in the white list -the object can be deserialized. If a package appears in both black list and white list, -the one in black list takes precedence. If a class neither matches with `black list` -nor with the `white list`, the class deserialization will be denied -unless the white list is empty (meaning the user doesn't specify the white list at all). +Artemis provides a simple class filtering mechanism with which a user can +specify which packages are to be trusted and which are not. Objects whose +classes are from trusted packages can be deserialized without problem, whereas +those from 'not trusted' packages will be denied deserialization. + +Artemis keeps a `black list` to keep track of packages that are not trusted and +a `white list` for trusted packages. By default both lists are empty, meaning +any serializable object is allowed to be deserialized. If an object whose class +matches one of the packages in black list, it is not allowed to be +deserialized. If it matches one in the white list the object can be +deserialized. If a package appears in both black list and white list, the one +in black list takes precedence. If a class neither matches with `black list` +nor with the `white list`, the class deserialization will be denied unless the +white list is empty (meaning the user doesn't specify the white list at all). A class is considered as a 'match' if -- its full name exactly matches one of the entries in the list. -- its package matches one of the entries in the list or is a sub-package of one of the entries. +- its full name exactly matches one of the entries in the list. +- its package matches one of the entries in the list or is a sub-package of one + of the entries. -For example, if a class full name is "org.apache.pkg1.Class1", some matching entries could be: +For example, if a class full name is "org.apache.pkg1.Class1", some matching +entries could be: -- `org.apache.pkg1.Class1` - exact match. -- `org.apache.pkg1` - exact package match. -- `org.apache` -- sub package match. +- `org.apache.pkg1.Class1` - exact match. +- `org.apache.pkg1` - exact package match. +- `org.apache` -- sub package match. A `*` means 'match-all' in a black or white list. ### Config via Connection Factories To specify the white and black lists one can use the URL parameters -`deserializationBlackList` and `deserializationWhiteList`. For example, -using JMS: +`deserializationBlackList` and `deserializationWhiteList`. For example, using +JMS: - ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0?deserializationBlackList=org.apache.pkg1,org.some.pkg2"); +```java +ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://0?deserializationBlackList=org.apache.pkg1,org.some.pkg2"); +``` The above statement creates a factory that has a black list contains two forbidden packages, "org.apache.pkg1" and "org.some.pkg2", separated by a @@ -833,46 +1063,59 @@ comma. ### Config via system properties -There are two system properties available for specifying black list and white list: +There are two system properties available for specifying black list and white +list: -- `org.apache.activemq.artemis.jms.deserialization.whitelist` - comma separated list of entries for the white list. -- `org.apache.activemq.artemis.jms.deserialization.blacklist` - comma separated list of entries for the black list. +- `org.apache.activemq.artemis.jms.deserialization.whitelist` - comma separated + list of entries for the white list. +- `org.apache.activemq.artemis.jms.deserialization.blacklist` - comma separated + list of entries for the black list. -Once defined, all JMS object message deserialization in the VM is subject to checks against the two lists. However if you create a ConnectionFactory -and set a new set of black/white lists on it, the new values will override the system properties. +Once defined, all JMS object message deserialization in the VM is subject to +checks against the two lists. However if you create a ConnectionFactory and set +a new set of black/white lists on it, the new values will override the system +properties. ### Config for resource adapters -Message beans using a JMS resource adapter to receive messages can also control their object deserialization via properly configuring relevant -properties for their resource adapters. There are two properties that you can configure with connection factories in a resource adapter: +Message beans using a JMS resource adapter to receive messages can also control +their object deserialization via properly configuring relevant properties for +their resource adapters. There are two properties that you can configure with +connection factories in a resource adapter: -- `deserializationBlackList` - comma separated values for black list -- `deserializationWhiteList` - comma separated values for white list +- `deserializationBlackList` - comma separated values for black list +- `deserializationWhiteList` - comma separated values for white list -These properties, once specified, are eventually set on the corresponding internal factories. +These properties, once specified, are eventually set on the corresponding +internal factories. ### Config for REST interface -Apache Artemis REST interface ([Rest](rest.md)) allows interactions between jms client and rest clients. -It uses JMS ObjectMessage to wrap the actual user data between the 2 types of clients and deserialization -is needed during this process. If you want to control the deserialization for REST, you need to set the -black/white lists for it separately as Apache Artemis REST Interface is deployed as a web application. -You need to put the black/white lists in its web.xml, as context parameters, as follows - - - - org.apache.activemq.artemis.jms.deserialization.whitelist - some.allowed.class - - - org.apache.activemq.artemis.jms.deserialization.blacklist - some.forbidden.class - - ... - +Apache Artemis REST interface ([Rest](rest.md)) allows interactions between jms +client and rest clients. It uses JMS ObjectMessage to wrap the actual user +data between the 2 types of clients and deserialization is needed during this +process. If you want to control the deserialization for REST, you need to set +the black/white lists for it separately as Apache Artemis REST Interface is +deployed as a web application. You need to put the black/white lists in its +web.xml, as context parameters, as follows + +```xml + + + org.apache.activemq.artemis.jms.deserialization.whitelist + some.allowed.class + + + org.apache.activemq.artemis.jms.deserialization.blacklist + some.forbidden.class + +... + +``` The param-value for each list is a comma separated string value representing the list. ## Masking Passwords -For details about masking passwords in broker.xml please see the [Masking Passwords](masking-passwords.md) chapter. +For details about masking passwords in broker.xml please see the [Masking +Passwords](masking-passwords.md) chapter. diff --git a/docs/user-manual/en/send-guarantees.md b/docs/user-manual/en/send-guarantees.md index 79b9097bd69..da631e47efc 100644 --- a/docs/user-manual/en/send-guarantees.md +++ b/docs/user-manual/en/send-guarantees.md @@ -32,15 +32,15 @@ has definitely reached the server, and a response has been sent back to the client. This can be configured individually for durable and non-durable messages, and is determined by the following two URL parameters: -- `blockOnDurableSend`. If this is set to `true` then all calls to - send for durable messages on non transacted sessions will block - until the message has reached the server, and a response has been - sent back. The default value is `true`. - -- `blockOnNonDurableSend`. If this is set to `true` then all calls to - send for non-durable messages on non transacted sessions will block - until the message has reached the server, and a response has been - sent back. The default value is `false`. +- `blockOnDurableSend`. If this is set to `true` then all calls to + send for durable messages on non transacted sessions will block + until the message has reached the server, and a response has been + sent back. The default value is `true`. + +- `blockOnNonDurableSend`. If this is set to `true` then all calls to + send for non-durable messages on non transacted sessions will block + until the message has reached the server, and a response has been + sent back. The default value is `false`. Setting block on sends to `true` can reduce performance since each send requires a network round trip before the next send can be performed. diff --git a/docs/user-manual/en/slow-consumers.md b/docs/user-manual/en/slow-consumers.md index 49d5141cc4b..fc85d38a009 100644 --- a/docs/user-manual/en/slow-consumers.md +++ b/docs/user-manual/en/slow-consumers.md @@ -36,5 +36,5 @@ the detection algorithm. See [thread pooling](thread-pooling.md) for more detail ## Example -See the [examples](examples.md) chapter for an example which shows how to detect a slow consumer +See the [slow consumer example](examples.md#slow-consumer) which shows how to detect a slow consumer with Apache ActiveMQ Artemis. diff --git a/docs/user-manual/en/spring-integration.md b/docs/user-manual/en/spring-integration.md index 9c7c9390592..f63df5c87a2 100644 --- a/docs/user-manual/en/spring-integration.md +++ b/docs/user-manual/en/spring-integration.md @@ -1,39 +1,16 @@ # Spring Integration Apache ActiveMQ Artemis provides a simple bootstrap class, -`org.apache.activemq.integration.spring.SpringJmsBootstrap`, for -integration with Spring. To use it, you configure Apache ActiveMQ Artemis as you always -would, through its various configuration files like -`broker.xml`. +`org.apache.activemq.artemis.integration.spring.SpringJmsBootstrap`, for +integration with Spring. To use it, you configure Apache ActiveMQ Artemis as +you always would, through its various configuration files like `broker.xml`. -Here we've specified a `javax.jms.ConnectionFactory` we want bound to a -`ConnectionFactory` entry as well as a queue destination bound to a -`/queue/exampleQueue` entry. Using the `SpringJmsBootStrap` bean will -automatically populate the Spring context with references to those beans -so that you can use them. Below is an example Spring JMS bean file -taking advantage of this feature: +The `SpringJmsBootstrap` class extends the EmbeddedJMS class talked about in +[embedding ActiveMQ](embedding-activemq.md) and the same defaults and +configuration options apply. See the javadocs for more details on other +properties of the bean class. -```xml - +## Example - - - - - - - - - - -``` - -As you can see, the `listenerContainer` bean references the components -defined in the `activemq-jms.xml` file. The `SpringJmsBootstrap` class -extends the EmbeddedJMS class talked about in [JMS API](embedding-activemq.md) and the same defaults -and configuration options apply. Also notice that an `init-method` must -be declared with a start value so that the bean's lifecycle is executed. -See the javadocs for more details on other properties of the bean class. +See the [Spring Integration Example](examples.md#spring-integration) for a +demonstration of how this can work. diff --git a/docs/user-manual/en/stomp.md b/docs/user-manual/en/stomp.md new file mode 100644 index 00000000000..8ba11f2bd1f --- /dev/null +++ b/docs/user-manual/en/stomp.md @@ -0,0 +1,293 @@ +# STOMP + +[STOMP](https://stomp.github.io/) is a text-orientated wire protocol that +allows STOMP clients to communicate with STOMP Brokers. Apache ActiveMQ Artemis +supports STOMP 1.0, 1.1 and 1.2. + +STOMP clients are available for several languages and platforms making it a +good choice for interoperability. + +By default there are `acceptor` elements configured to accept STOMP connections +on ports `61616` and `61613`. + +See the general [Protocols and Interoperability](protocols-interoperability.md) +chapter for details on configuring an `acceptor` for STOMP. + +Refer to the STOMP examples for a look at some of this functionality in action. + +## Limitations + +The STOMP specification identifies **transactional acknowledgements** as an +optional feature. Support for transactional acknowledgements is not implemented +in Apache ActiveMQ Artemis. The `ACK` frame can not be part of a transaction. +It will be ignored if its `transaction` header is set. + +## Virtual Hosting + +Apache ActiveMQ Artemis currently doesn't support virtual hosting, which means +the `host` header in `CONNECT` frame will be ignored. + +## Mapping STOMP destinations to addresses and queues + +STOMP clients deals with *destinations* when sending messages and subscribing. +Destination names are simply strings which are mapped to some form of +destination on the server - how the server translates these is left to the +server implementation. + +In Apache ActiveMQ Artemis, these destinations are mapped to *addresses* and +*queues* depending on the operation being done and the desired semantics (e.g. +anycast or multicast). + +## Sending + +When a STOMP client sends a message (using a `SEND` frame), the protocol +manager looks at the message to determine where to route it and potentially how +to create the address and/or queue to which it is being sent. The protocol +manager uses either of the following bits of information from the frame to +determine the routing type: + +1. The value of the `destination-type` header. Valid values are `ANYCAST` and + `MULTICAST` (case sensitive). + +2. The "prefix" on the `destination` header. See [additional + info](address-model.md#using-prefixes-to-determine-routing-type) on + prefixes. + +If no indication of routing type is supplied then anycast semantics are used. + +The `destination` header maps to an address of the same name. If the +`destination` header used a prefix then the prefix is stripped. + +## Subscribing + +When a STOMP client subscribes to a destination (using a `SUBSCRIBE` frame), +the protocol manager looks at the frame to determine what subscription +semantics to use and potentially how to create the address and/or queue for the +subscription. The protocol manager uses either of the following bits of +information from the frame to determine the routing type: + +1. The value of the `subscription-type` header. Valid values are `ANYCAST` and + `MULTICAST` (case sensitive). + +2. The "prefix" on the `destination` header. See [additional + info](address-model.md#using-prefixes-to-determine-routing-type) on + prefixes. + +If no indication of routing type is supplied then anycast semantics are used. + +The `destination` header maps to an address of the same name if multicast is +used or to a queue of the same name if anycast is used. If the `destination` +header used a prefix then the prefix is stripped. + +## STOMP heart-beating and connection-ttl + +Well behaved STOMP clients will always send a `DISCONNECT` frame before closing +their connections. In this case the server will clear up any server side +resources such as sessions and consumers synchronously. However if STOMP +clients exit without sending a `DISCONNECT` frame or if they crash the server +will have no way of knowing immediately whether the client is still alive or +not. STOMP connections therefore default to a `connection-ttl` value of 1 +minute (see chapter on [connection-ttl](connection-ttl.md) for more +information. This value can be overridden using the `connection-ttl-override` +property or if you need a specific connectionTtl for your stomp connections +without affecting the broker-wide `connection-ttl-override` setting, you can +configure your stomp acceptor with the `connectionTtl` property, which is used +to set the ttl for connections that are created from that acceptor. For +example: + +```xml +tcp://localhost:61613?protocols=STOMP;connectionTtl=20000 +``` + +The above configuration will make sure that any STOMP connection that is +created from that acceptor and does not include a `heart-beat` header or +disables client-to-server heart-beats by specifying a `0` value will have its +`connection-ttl` set to 20 seconds. The `connectionTtl` set on an acceptor will +take precedence over `connection-ttl-override`. The default `connectionTtl` is +60,000 milliseconds. + +Since STOMP 1.0 does not support heart-beating then all connections from STOMP +1.0 clients will have a connection TTL imposed upon them by the broker based on +the aforementioned configuration options. Likewise, any STOMP 1.1 or 1.2 +clients that don't specify a `heart-beat` header or disable client-to-server +heart-beating (e.g. by sending `0,X` in the `heart-beat` header) will have a +connection TTL imposed upon them by the broker. + +For STOMP 1.1 and 1.2 clients which send a non-zero client-to-server +`heart-beat` header value then their connection TTL will be set accordingly. +However, the broker will not strictly set the connection TTL to the same value +as the specified in the `heart-beat` since even small network delays could then +cause spurious disconnects. Instead, the client-to-server value in the +`heart-beat` will be multiplied by the `heartBeatConnectionTtlModifer` +specified on the acceptor. The `heartBeatConnectionTtlModifer` is a decimal +value that defaults to `2.0` so for example, if a client sends a `heart-beat` +header of `1000,0` the the connection TTL will be set to `2000` so that the +data or ping frames sent every 1000 milliseconds will have a sufficient cushion +so as not to be considered late and trigger a disconnect. This is also in +accordance with the STOMP 1.1 and 1.2 specifications which both state, "because +of timing inaccuracies, the receiver SHOULD be tolerant and take into account +an error margin." + +The minimum and maximum connection TTL allowed can also be specified on the +acceptor via the `connectionTtlMin` and `connectionTtlMax` properties +respectively. The default `connectionTtlMin` is 1000 and the default +`connectionTtlMax` is Java's `Long.MAX_VALUE` meaning there essentially is no +max connection TTL by default. Keep in mind that the +`heartBeatConnectionTtlModifer` is relevant here. For example, if a client +sends a `heart-beat` header of `20000,0` and the acceptor is using a +`connectionTtlMax` of `30000` and a default `heartBeatConnectionTtlModifer` of +`2.0` then the connection TTL would be `40000` (i.e. `20000` * `2.0`) which +would exceed the `connectionTtlMax`. In this case the server would respond to +the client with a `heart-beat` header of `0,15000` (i.e. `30000` / `2.0`). As +described previously, this is to make sure there is a sufficient cushion for +the client heart-beats in accordance with the STOMP 1.1 and 1.2 specifications. +The same kind of calculation is done for `connectionTtlMin`. + +The minimum server-to-client heart-beat value is 500ms. + +> **Note:** +> +> Please note that the STOMP protocol version 1.0 does not contain any +> heart-beat frame. It is therefore the user's responsibility to make sure data +> is sent within connection-ttl or the server will assume the client is dead +> and clean up server side resources. With STOMP 1.1 users can use heart-beats +> to maintain the life cycle of stomp connections. + +## Selector/Filter expressions + +STOMP subscribers can specify an expression used to select or filter what the +subscriber receives using the `selector` header. The filter expression syntax +follows the *core filter syntax* described in the [Filter +Expressions](filter-expressions.md) documentation. + +## STOMP and JMS interoperability + +### Sending and consuming STOMP message from JMS or Core API + +STOMP is mainly a text-orientated protocol. To make it simpler to interoperate +with JMS and Core API, our STOMP implementation checks for presence of the +`content-length` header to decide how to map a STOMP 1.0 message to a JMS +Message or a Core message. + +If the STOMP 1.0 message does *not* have a `content-length` header, it will be +mapped to a JMS *TextMessage* or a Core message with a *single nullable +SimpleString in the body buffer*. + +Alternatively, if the STOMP 1.0 message *has* a `content-length` header, it +will be mapped to a JMS *BytesMessage* or a Core message with a *byte[] in the +body buffer*. + +The same logic applies when mapping a JMS message or a Core message to STOMP. A +STOMP 1.0 client can check the presence of the `content-length` header to +determine the type of the message body (String or bytes). + +### Message IDs for STOMP messages + +When receiving STOMP messages via a JMS consumer or a QueueBrowser, the +messages have no properties like JMSMessageID by default. However this may +bring some inconvenience to clients who wants an ID for their purpose. The +broker STOMP provides a parameter to enable message ID on each incoming STOMP +message. If you want each STOMP message to have a unique ID, just set the +`stompEnableMessageId` to true. For example: + +```xml +tcp://localhost:61613?protocols=STOMP;stompEnableMessageId=true +``` + +When the server starts with the above setting, each stomp message sent through +this acceptor will have an extra property added. The property key is +`amq-message-id` and the value is a String representation of a long type +internal message id prefixed with `STOMP`, like: + +``` +amq-message-id : STOMP12345 +``` + +The default `stomp-enable-message-id` value is `false`. + +## Durable Subscriptions + +The `SUBSCRIBE` and `UNSUBSCRIBE` frames can be augmented with special headers +to create and destroy durable subscriptions respectively. + +To create a durable subscription the `client-id` header must be set on the +`CONNECT` frame and the `durable-subscription-name` must be set on the +`SUBSCRIBE` frame. The combination of these two headers will form the identity +of the durable subscription. + +To delete a durable subscription the `client-id` header must be set on the +`CONNECT` frame and the `durable-subscription-name` must be set on the +`UNSUBSCRIBE` frame. The values for these headers should match what was set on +the `SUBSCRIBE` frame to delete the corresponding durable subscription. + +It is possible to pre-configure durable subscriptions since the STOMP +implementation creates the queue used for the durable subscription in a +deterministic way (i.e. using the format of `client-id`.`subscription-name`). +For example, if you wanted to configure a durable subscription on the address +`myAddress` with a client-id of `myclientid` and a subscription name of +`mysubscription` then configure the durable subscription: + +```xml + +
+ + + +
+
+``` + +## Handling of Large Messages with STOMP + +STOMP clients may send very large frame bodies which can exceed the size of the +broker's internal buffer, causing unexpected errors. To prevent this situation +from happening, the broker provides a STOMP configuration attribute +`stompMinLargeMessageSize`. This attribute can be configured inside a stomp +acceptor, as a parameter. For example: + +```xml +tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240 +``` + +The type of this attribute is integer. When this attributed is configured, the +broker will check the size of the body of each STOMP frame arrived from +connections established with this acceptor. If the size of the body is equal or +greater than the value of `stompMinLargeMessageSize`, the message will be +persisted as a large message. When a large message is delievered to a STOMP +consumer, the broker will automatically handle the conversion from a large +message to a normal message, before sending it to the client. + +If a large message is compressed, the server will uncompressed it before +sending it to stomp clients. The default value of `stompMinLargeMessageSize` is +the same as the default value of +[min-large-message-size](large-messages.md#configuring-parameters). + +## Web Sockets + +Apache ActiveMQ Artemis also support STOMP over [Web +Sockets](https://html.spec.whatwg.org/multipage/web-sockets.html). Modern web +browsers which support Web Sockets can send and receive STOMP messages. + +STOMP over Web Sockets is supported via the normal STOMP acceptor: + +```xml +tcp://localhost:61614?protocols=STOMP +``` + +With this configuration, Apache ActiveMQ Artemis will accept STOMP connections +over Web Sockets on the port `61614`. Web browsers can then connect to +`ws://:61614` using a Web Socket to send and receive STOMP messages. + +A companion JavaScript library to ease client-side development is available +from [GitHub](https://github.com/jmesnil/stomp-websocket) (please see its +[documentation](http://jmesnil.net/stomp-websocket/doc/) for a complete +description). + +The payload length of Web Socket frames can vary between client +implementations. By default the broker will accept frames with a payload length +of 65,536. If the client needs to send payloads longer than this in a single +frame this length can be adjusted by using the `stompMaxFramePayloadLength` URL +parameter on the acceptor. + +The `stomp-websockets` example shows how to configure an Apache ActiveMQ +Artemis broker to have web browsers and Java applications exchanges messages. diff --git a/docs/user-manual/en/syntax.md b/docs/user-manual/en/syntax.md index 71db615ea8d..c37820d5b77 100644 --- a/docs/user-manual/en/syntax.md +++ b/docs/user-manual/en/syntax.md @@ -11,7 +11,7 @@ Somejava s = new SomeJava(); ``` -> **Note** +> **Note:** > > This is a Note diff --git a/docs/user-manual/en/tools.md b/docs/user-manual/en/tools.md deleted file mode 100644 index 4a6aa0de02a..00000000000 --- a/docs/user-manual/en/tools.md +++ /dev/null @@ -1,216 +0,0 @@ -# Tools - -You can use the artemis cli interface to execute data maintenance tools: - -This is a list of sub-commands available - -Name | Description -:--- | :--- -exp | Export the message data using a special and independent XML format -imp | Imports the journal to a running broker using the output from expt -data | Prints a report about journal records and summary of existent records, as well a report on paging -encode | shows an internal format of the journal encoded to String -decode | imports the internal journal format from encode - -You can use the help at the tool for more information on how to execute each of the tools. For example: - -``` -$ ./artemis help data print -NAME - artemis data print - Print data records information (WARNING: don't use - while a production server is running) - -SYNOPSIS - artemis data print [--bindings ] [--journal ] - [--paging ] - -OPTIONS - --bindings - The folder used for bindings (default ../data/bindings) - - --journal - The folder used for messages journal (default ../data/journal) - - --paging - The folder used for paging (default ../data/paging) - - -``` - - -For a full list of data tools commands available use: - -``` -NAME - artemis data - data tools group - (print|imp|exp|encode|decode|compact) (example ./artemis data print) - -SYNOPSIS - artemis data - artemis data compact [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - artemis data decode [--broker ] [--suffix ] - [--verbose] [--paging ] [--prefix ] [--file-size ] - [--directory ] --input [--journal ] - [--large-messages ] [--bindings ] - artemis data encode [--directory ] [--broker ] - [--suffix ] [--verbose] [--paging ] [--prefix ] - [--file-size ] [--journal ] - [--large-messages ] [--bindings ] - artemis data exp [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - artemis data imp [--host ] [--verbose] [--port ] - [--password ] [--transaction] --input [--user ] - artemis data print [--broker ] [--verbose] - [--paging ] [--journal ] - [--large-messages ] [--bindings ] - -COMMANDS - With no arguments, Display help information - - print - Print data records information (WARNING: don't use while a - production server is running) - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - exp - Export all message-data using an XML that could be interpreted by - any system. - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - imp - Import all message-data using an XML that could be interpreted by - any system. - - With --host option, The host used to import the data (default - localhost) - - With --verbose option, Adds more information on the execution - - With --port option, The port used to import the data (default 61616) - - With --password option, User name used to import the data. (default - null) - - With --transaction option, If this is set to true you will need a - whole transaction to commit at the end. (default false) - - With --input option, The input file name (default=exp.dmp) - - With --user option, User name used to import the data. (default - null) - - decode - Decode a journal's internal format into a new journal set of files - - With --broker option, This would override the broker configuration - from the bootstrap - - With --suffix option, The journal suffix (default amq) - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --prefix option, The journal prefix (default activemq-data) - - With --file-size option, The journal size (default 10485760) - - With --directory option, The journal folder (default journal folder - from broker.xml) - - With --input option, The input file name (default=exp.dmp) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - encode - Encode a set of journal files into an internal encoded data format - - With --directory option, The journal folder (default the journal - folder from broker.xml) - - With --broker option, This would override the broker configuration - from the bootstrap - - With --suffix option, The journal suffix (default amq) - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --prefix option, The journal prefix (default activemq-data) - - With --file-size option, The journal size (default 10485760) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) - - compact - Compacts the journal of a non running server - - With --broker option, This would override the broker configuration - from the bootstrap - - With --verbose option, Adds more information on the execution - - With --paging option, The folder used for paging (default from - broker.xml) - - With --journal option, The folder used for messages journal (default - from broker.xml) - - With --large-messages option, The folder used for large-messages - (default from broker.xml) - - With --bindings option, The folder used for bindings (default from - broker.xml) -``` \ No newline at end of file diff --git a/docs/user-manual/en/undelivered-messages.md b/docs/user-manual/en/undelivered-messages.md index 6156c8f8e08..5ef5565085b 100644 --- a/docs/user-manual/en/undelivered-messages.md +++ b/docs/user-manual/en/undelivered-messages.md @@ -8,18 +8,18 @@ in the queue indefinitely, clogging the system. There are 2 ways to deal with these undelivered messages: -- Delayed redelivery. +- Delayed redelivery. - It is possible to delay messages redelivery. This gives the client some - time to recover from any transient failures and to prevent overloading - its network or CPU resources. + It is possible to delay messages redelivery. This gives the client some + time to recover from any transient failures and to prevent overloading + its network or CPU resources. -- Dead Letter Address. +- Dead Letter Address. - It is also possible to configure a dead letter address so that after - a specified number of unsuccessful deliveries, messages are removed - from their queue and sent to the dead letter address. These messages - will not be delivered again from this queue. + It is also possible to configure a dead letter address so that after + a specified number of unsuccessful deliveries, messages are removed + from their queue and sent to the dead letter address. These messages + will not be delivered again from this queue. Both options can be combined for maximum flexibility. @@ -130,15 +130,15 @@ set of addresses (see [Understanding the Wildcard Syntax](wildcard-syntax.md)). Dead letter messages which are consumed from a dead letter address have the following properties: -- `_AMQ_ORIG_ADDRESS` +- `_AMQ_ORIG_ADDRESS` - a String property containing the *original address* of the dead - letter message + a String property containing the *original address* of the dead + letter message -- `_AMQ_ORIG_QUEUE` +- `_AMQ_ORIG_QUEUE` - a String property containing the *original queue* of the dead letter - message + a String property containing the *original queue* of the dead letter + message ### Example diff --git a/docs/user-manual/en/unit-testing.md b/docs/user-manual/en/unit-testing.md index 7beb311cfe0..859ec007ac1 100644 --- a/docs/user-manual/en/unit-testing.md +++ b/docs/user-manual/en/unit-testing.md @@ -1,29 +1,26 @@ # Unit Testing -The package ```artemis-junit``` provides tools to facilitate how to run Artemis resources inside Junit Tests. +The package `artemis-junit` provides tools to facilitate how to run Artemis resources inside JUnit Tests. -These are provided as junit rules and can make it easier to embed Messaging functionality on your tests. +These are provided as JUnit "rules" and can make it easier to embed messaging functionality on your tests. ## Example - ### Import this on your pom.xml ```xml - org.apache.activemq - artemis-junit - - 1.5.0 - test + org.apache.activemq + artemis-junit + + 2.5.0 + test ``` - ### Declare a rule on your JUnit Test - ```java import org.apache.activemq.artemis.junit.EmbeddedJMSResource; import org.junit.Rule; @@ -44,34 +41,31 @@ public class MyTest { This will start a server that will be available for your test: ``` -ain] 17:00:16,644 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging) +[main] 17:00:16,644 INFO [org.apache.activemq.artemis.core.server] AMQ221000: live Message Broker is starting with configuration Broker Configuration (clustered=false,journalDirectory=data/journal,bindingsDirectory=data/bindings,largeMessagesDirectory=data/largemessages,pagingDirectory=data/paging) [main] 17:00:16,666 INFO [org.apache.activemq.artemis.core.server] AMQ221045: libaio is not available, switching the configuration into NIO [main] 17:00:16,688 INFO [org.apache.activemq.artemis.core.server] AMQ221043: Protocol module found: [artemis-server]. Adding protocol support for: CORE [main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live [main] 17:00:16,801 INFO [org.apache.activemq.artemis.core.server] AMQ221001: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [embedded-jms-server, nodeID=39e78380-842c-11e6-9e43-f45c8992f3c7] [main] 17:00:16,891 INFO [org.apache.activemq.artemis.core.server] AMQ221002: Apache ActiveMQ Artemis Message Broker version 1.5.0-SNAPSHOT [39e78380-842c-11e6-9e43-f45c8992f3c7] stopped, uptime 0.272 seconds - ``` - ### Ordering rules -This is actually a Junit feature, but this could be helpful on pre-determining the order on which rules are executed. +This is actually a JUnit feature, but this could be helpful on pre-determining the order on which rules are executed. ```java - ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL()); - - @Rule - public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer); +ActiveMQDynamicProducerResource producer = new ActiveMQDynamicProducerResource(server.getVmURL()); +@Rule +public RuleChain ruleChain = RuleChain.outerRule(new ThreadLeakCheckRule()).around(server).around(producer); ``` ### Available Rules Name | Description -:--- | :--- -EmbeddedActiveMQResource | It will run a Server, without the JMS manager -EmbeddedJMSResource | It will run a Server, including the JMS Manager -ActiveMQConsumerResource | It will automate the creation of a consumer -ActiveMQProducerResource | It will automate the creation of a producer -ThreadLeakCheckRule | It will check that all threads have been finished after the test is finished +--- | --- +EmbeddedActiveMQResource | Run a Server, without the JMS manager +EmbeddedJMSResource | Run a Server, including the JMS Manager +ActiveMQConsumerResource | Automate the creation of a consumer +ActiveMQProducerResource | Automate the creation of a producer +ThreadLeakCheckRule | Check that all threads have been finished after the test is finished diff --git a/docs/user-manual/en/upgrading.md b/docs/user-manual/en/upgrading.md index e564f49b2d2..61609b8c9b3 100644 --- a/docs/user-manual/en/upgrading.md +++ b/docs/user-manual/en/upgrading.md @@ -1,38 +1,41 @@ # Upgrading the Broker -Apache ActiveMQ 5.x (and previous versions) is runnable out of the box by executing -the command: `./bin/activemq run`. The ActiveMQ Artemis broker follows a different -paradigm where the project distribution serves as the broker "home" and one or more -broker "instances" are created which reference the "home" for resources (e.g. jar files) -which can be safely shared between broker instances. Therefore, an instance of the broker -must be created before it can be run. This may seems like an overhead at first -glance, but it becomes very practical when updating to a new Artemis version for example. - -To create an Artemis broker instance navigate into the Artemis home folder and run: -`./bin/artemis create /path/to/myBrokerInstance` on the command line. - -> **Note** -> -> It's recommended to choose a folder different than the on where Apache Artemis was -> downloaded. This separation allows you run multiple broker instances with the same -> Artemis "home" for example. It also simplifies updating to newer versions of Artemis. +Apache ActiveMQ 5.x (and previous versions) is runnable out of the box by +executing the command: `./bin/activemq run`. The ActiveMQ Artemis broker +follows a different paradigm where the project distribution serves as the +broker "home" and one or more broker "instances" are created which reference +the "home" for resources (e.g. jar files) which can be safely shared between +broker instances. Therefore, an instance of the broker must be created before +it can be run. This may seems like an overhead at first glance, but it becomes +very practical when updating to a new Artemis version for example. + +To create an Artemis broker instance navigate into the Artemis home folder and +run: `./bin/artemis create /path/to/myBrokerInstance` on the command line. -Because of this separation it's very easy to upgrade Artemis in most cases. +Because of this separation it's very easy to upgrade Artemis in most cases. + +> **Note:** +> +> It's recommended to choose a folder different than the on where Apache +> Artemis was downloaded. This separation allows you run multiple broker +> instances with the same Artemis "home" for example. It also simplifies +> updating to newer versions of Artemis. ## General Upgrade Procedure -Upgrading may require some specific steps noted in the [versions](versions.md), but the -general process is as follows: +Upgrading may require some specific steps noted in the [versions](versions.md), +but the general process is as follows: 1. Navigate to the `etc` folder of the broker instance that's being upgraded -1. Open `artemis.profile` (`artemis.profile.cmd` on Windows). It contains a property - which is relevant for the upgrade: +1. Open `artemis.profile` (`artemis.profile.cmd` on Windows). It contains a + property which is relevant for the upgrade: ``` ARTEMIS_HOME='/path/to/apache-artemis-version' ``` -The `ARTEMIS_HOME` property is used to link the instance with the home. -_In most cases_ the instance can be upgraded to a newer version simply by changing the -value of this property to the location of the new broker home. Please refer to the -aforementioned [versions](versions.md) document for additional upgrade steps (if required). \ No newline at end of file +The `ARTEMIS_HOME` property is used to link the instance with the home. _In +most cases_ the instance can be upgraded to a newer version simply by changing +the value of this property to the location of the new broker home. Please refer +to the aforementioned [versions](versions.md) document for additional upgrade +steps (if required). diff --git a/docs/user-manual/en/using-AMQP.md b/docs/user-manual/en/using-AMQP.md deleted file mode 100644 index 49d04782179..00000000000 --- a/docs/user-manual/en/using-AMQP.md +++ /dev/null @@ -1,74 +0,0 @@ -# Using AMQP - -Apache ActiveMQ Artemis is also a pure AMQP 1.0 broker, with a high performant and feature complete protocol manager for AMQP. - -You can use *any* AMQP 1.0 compatible clients. - -A short list includes: - -- qpid clients at the [qpid project](https://qpid.apache.org/download.html) -- [.NET Clients](https://blogs.apache.org/activemq/entry/using-net-libraries-with-activemq) -- [Javascript NodeJS](https://github.com/noodlefrenzy/node-amqp10) -- [Java Script RHEA](https://github.com/grs/rhea) - - -... and many others. - - -# Message Conversions - -The broker will not perform any message conversion to any other protocols when sending AMQP and receiving AMQP. - -However if you intend your message to be received on a AMQP JMS Client, you must follow the JMS Mapping convention: - -- [JMS Mapping Conventions](https://www.oasis-open.org/committees/download.php/53086/amqp-bindmap-jms-v1.0-wd05.pdf) - - -If you send a body type that is not recognized by this specification the conversion between AMQP and any other protocol will make it a Binary Message. - -So, make sure you follow these conventions if you intend to cross protocols or languages. Especially on the message body. - -A compatibility setting, allows aligning the naming convention of AMQP queues (JMS Durable and Shared Subscriptions) with CORE. -For backwards compatibility reasons, you need to explicitly enable this via broker configuration: - -* amqp-use-core-subscription-naming - * true - use queue naming convention that is aligned with CORE. - * false (DEFAULT) - use older naming convention. - - -# Example - -We have a few examples as part of the Artemis distribution: - - -- .NET: - * ./examples/protocols/amqp/dotnet - -- ProtonCPP - * ./examples/protocols/amqp/proton-cpp - -- Ruby - * ./examples/protocols/amqp/proton-ruby - -- Java (Using the qpid JMS Client) - * ./examples/protocols/amqp/queue - -- Interceptors - * ./examples/features/standard/broker-plugin - - - # Intercepting and changing messages - - We don't recommend changing messages at the server's side for a few reasons: - - - AMQPMessages are meant to be immutable - - The message won't be the original message the user sent - - AMQP has the possibility of signing messages. The signature would be broken. - - For performance reasons. We try not to re-encode (or even decode) messages. - -If regardless these recommendations you still need and want to intercept and change AMQP Messages, look at the example under ./examples/features/standard/broker-plugin. - -This example will send AMQP Message and modify properties before they reach the journals and are sent to the consumers. - - - diff --git a/docs/user-manual/en/using-core.md b/docs/user-manual/en/using-core.md deleted file mode 100644 index 78c08a5589c..00000000000 --- a/docs/user-manual/en/using-core.md +++ /dev/null @@ -1,212 +0,0 @@ -# Using Core - -Apache ActiveMQ Artemis core is a completely JMS-agnostic messaging system with its own -API. We call this the *core API*. - -If you don't want to use JMS or other protocols you can use the core API directly. The core -API provides all the functionality of JMS but without much of the -complexity. It also provides features that are not available using JMS. - -## Core Messaging Concepts - -Some of the core messaging concepts are similar to JMS concepts, but -core messaging concepts differ in some ways. In general the core -messaging API is simpler than the JMS API, since we remove distinctions -between queues, topics and subscriptions. We'll discuss each of the -major core messaging concepts in turn, but to see the API in detail, -please consult the Javadoc. - -### Message - -- A message is the unit of data which is sent between clients and - servers. - -- A message has a body which is a buffer containing convenient methods - for reading and writing data into it. - -- A message has a set of properties which are key-value pairs. Each - property key is a string and property values can be of type integer, - long, short, byte, byte[], String, double, float or boolean. - -- A message has an *address* it is being sent to. When the message - arrives on the server it is routed to any queues that are bound to - the address - if the queues are bound with any filter, the message - will only be routed to that queue if the filter matches. An address - may have many queues bound to it or even none. There may also be - entities other than queues, like *diverts* bound to addresses. - -- Messages can be either durable or non durable. Durable messages in a - durable queue will survive a server crash or restart. Non durable - messages will never survive a server crash or restart. - -- Messages can be specified with a priority value between 0 and 9. 0 - represents the lowest priority and 9 represents the highest. - Apache ActiveMQ Artemis will attempt to deliver higher priority messages before - lower priority ones. - -- Messages can be specified with an optional expiry time. Apache ActiveMQ Artemis - will not deliver messages after its expiry time has been exceeded. - -- Messages also have an optional timestamp which represents the time - the message was sent. - -- Apache ActiveMQ Artemis also supports the sending/consuming of very large messages - much larger than can fit in available RAM at any one time. - -### Address - -A server maintains a mapping between an address and a set of queues. -Zero or more queues can be bound to a single address. Each queue can be -bound with an optional message filter. When a message is routed, it is -routed to the set of queues bound to the message's address. If any of -the queues are bound with a filter expression, then the message will -only be routed to the subset of bound queues which match that filter -expression. - -Other entities, such as *diverts* can also be bound to an address and -messages will also be routed there. - -> **Note** -> -> In core, there is no concept of a Topic, Topic is a JMS only term. -> Instead, in core, we just deal with *addresses* and *queues*. -> -> For example, a JMS topic would be implemented by a single address to -> which many queues are bound. Each queue represents a subscription of -> the topic. A JMS Queue would be implemented as a single address to -> which one queue is bound - that queue represents the JMS queue. - -### Queue - -Queues can be durable, meaning the messages they contain survive a -server crash or restart, as long as the messages in them are durable. -Non durable queues do not survive a server restart or crash even if the -messages they contain are durable. - -Queues can also be temporary, meaning they are automatically deleted -when the client connection is closed, if they are not explicitly deleted -before that. - -Queues can be bound with an optional filter expression. If a filter -expression is supplied then the server will only route messages that -match that filter expression to any queues bound to the address. - -Many queues can be bound to a single address. A particular queue is only -bound to a maximum of one address. - -### ServerLocator - -Clients use `ServerLocator` instances to create `ClientSessionFactory` -instances. `ServerLocator` instances are used to locate servers and -create connections to them. - -In JMS terms think of a `ServerLocator` in the same way you would a JMS -Connection Factory. - -`ServerLocator` instances are created using the `ActiveMQClient` factory -class. - -### ClientSessionFactory - -Clients use `ClientSessionFactory` instances to create `ClientSession` -instances. `ClientSessionFactory` instances are basically the connection -to a server - -In JMS terms think of them as JMS Connections. - -`ClientSessionFactory` instances are created using the `ServerLocator` -class. - -### ClientSession - -A client uses a ClientSession for consuming and producing messages and -for grouping them in transactions. ClientSession instances can support -both transactional and non transactional semantics and also provide an -`XAResource` interface so messaging operations can be performed as part -of a -[JTA](http://www.oracle.com/technetwork/java/javaee/tech/jta-138684.html) -transaction. - -ClientSession instances group ClientConsumers and ClientProducers. - -ClientSession instances can be registered with an optional -`SendAcknowledgementHandler`. This allows your client code to be -notified asynchronously when sent messages have successfully reached the -server. This unique Apache ActiveMQ Artemis feature, allows you to have full guarantees -that sent messages have reached the server without having to block on -each message sent until a response is received. Blocking on each -messages sent is costly since it requires a network round trip for each -message sent. By not blocking and receiving send acknowledgements -asynchronously you can create true end to end asynchronous systems which -is not possible using the standard JMS API. For more information on this -advanced feature please see the section [Guarantees of sends and commits](send-guarantees.md). - -### ClientConsumer - -Clients use `ClientConsumer` instances to consume messages from a queue. -Core Messaging supports both synchronous and asynchronous message -consumption semantics. `ClientConsumer` instances can be configured with -an optional filter expression and will only consume messages which match -that expression. - -### ClientProducer - -Clients create `ClientProducer` instances on `ClientSession` instances -so they can send messages. ClientProducer instances can specify an -address to which all sent messages are routed, or they can have no -specified address, and the address is specified at send time for the -message. - -> **Warning** -> -> Please note that ClientSession, ClientProducer and ClientConsumer -> instances are *designed to be re-used*. -> -> It's an anti-pattern to create new ClientSession, ClientProducer and -> ClientConsumer instances for each message you produce or consume. If -> you do this, your application will perform very poorly. This is -> discussed further in the section on performance tuning [Performance Tuning](perf-tuning.md). - -## A simple example of using Core - -Here's a very simple program using the core messaging API to send and -receive a message. Logically it's comprised of two sections: firstly -setting up the producer to write a message to an *addresss*, and -secondly, creating a *queue* for the consumer, creating the consumer and -*starting* it. -```java -ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration( - InVMConnectorFactory.class.getName())); - -// In this simple example, we just use one session for both producing and receiving - -ClientSessionFactory factory = locator.createClientSessionFactory(); -ClientSession session = factory.createSession(); - -// A producer is associated with an address ... - -ClientProducer producer = session.createProducer("example"); -ClientMessage message = session.createMessage(true); -message.getBodyBuffer().writeString("Hello"); - -// We need a queue attached to the address ... - -session.createQueue("example", "example", true); - -// And a consumer attached to the queue ... - -ClientConsumer consumer = session.createConsumer("example"); - -// Once we have a queue, we can send the message ... - -producer.send(message); - -// We need to start the session before we can -receive- messages ... - -session.start(); -ClientMessage msgReceived = consumer.receive(); - -System.out.println("message = " + msgReceived.getBodyBuffer().readString()); - -session.close(); -``` diff --git a/docs/user-manual/en/using-jms.md b/docs/user-manual/en/using-jms.md index bf654b918c4..bc9f3f2729e 100644 --- a/docs/user-manual/en/using-jms.md +++ b/docs/user-manual/en/using-jms.md @@ -1,186 +1,193 @@ # Using JMS -Although Apache ActiveMQ Artemis provides a JMS agnostic messaging API, many users will -be more comfortable using JMS. +Although Apache ActiveMQ Artemis provides a JMS agnostic messaging API, many +users will be more comfortable using JMS. -JMS is a very popular API standard for messaging, and most messaging -systems provide a JMS API. If you are completely new to JMS we suggest -you follow the [Oracle JMS tutorial](https://docs.oracle.com/javaee/7/tutorial/partmessaging.htm) - -a full JMS tutorial is out of scope for this guide. +JMS is a very popular API standard for messaging, and most messaging systems +provide a JMS API. If you are completely new to JMS we suggest you follow the +[Oracle JMS +tutorial](https://docs.oracle.com/javaee/7/tutorial/partmessaging.htm) - a full +JMS tutorial is out of scope for this guide. Apache ActiveMQ Artemis also ships with a wide range of examples, many of which -demonstrate JMS API usage. A good place to start would be to play around -with the simple JMS Queue and Topic example, but we also provide -examples for many other parts of the JMS API. A full description of the -examples is available in [Examples](examples.md). +demonstrate JMS API usage. A good place to start would be to play around with +the simple JMS Queue and Topic example, but we also provide examples for many +other parts of the JMS API. A full description of the examples is available in +[Examples](examples.md). -In this section we'll go through the main steps in configuring the -server for JMS and creating a simple JMS program. We'll also show how to -configure and use JNDI, and also how to use JMS with Apache ActiveMQ Artemis without -using any JNDI. +In this section we'll go through the main steps in configuring the server for +JMS and creating a simple JMS program. We'll also show how to configure and use +JNDI, and also how to use JMS with Apache ActiveMQ Artemis without using any +JNDI. ## A simple ordering system For this chapter we're going to use a very simple ordering system as our -example. It is a somewhat contrived example because of its extreme -simplicity, but it serves to demonstrate the very basics of setting up -and using JMS. - -We will have a single JMS Queue called `OrderQueue`, and we will have a -single `MessageProducer` sending an order message to the queue and a -single `MessageConsumer` consuming the order message from the queue. - -The queue will be a `durable` queue, i.e. it will survive a server -restart or crash. We also want to pre-deploy the queue, i.e. specify the -queue in the server configuration so it is created automatically -without us having to explicitly create it from the client. - -## JNDI Configuration - -The JMS specification establishes the convention that *administered -objects* (i.e. JMS queue, topic and connection factory instances) are -made available via the JNDI API. Brokers are free to implement JNDI as -they see fit assuming the implementation fits the API. Apache ActiveMQ Artemis does not -have a JNDI server. Rather, it uses a client-side JNDI implementation -that relies on special properties set in the environment to construct -the appropriate JMS objects. In other words, no objects are stored in -JNDI on the Apache ActiveMQ Artemis server, instead they are simply instantiated on the -client based on the provided configuration. Let's look at the different -kinds of administered objects and how to configure them. - -> **Note** +example. It is a somewhat contrived example because of its extreme simplicity, +but it serves to demonstrate the very basics of setting up and using JMS. + +We will have a single JMS Queue called `OrderQueue`, and we will have a single +`MessageProducer` sending an order message to the queue and a single +`MessageConsumer` consuming the order message from the queue. + +The queue will be a `durable` queue, i.e. it will survive a server restart or +crash. We also want to pre-deploy the queue, i.e. specify the queue in the +server configuration so it is created automatically without us having to +explicitly create it from the client. + +## JNDI + +The JMS specification establishes the convention that *administered objects* +(i.e. JMS queue, topic and connection factory instances) are made available via +the JNDI API. Brokers are free to implement JNDI as they see fit assuming the +implementation fits the API. Apache ActiveMQ Artemis does not have a JNDI +server. Rather, it uses a client-side JNDI implementation that relies on +special properties set in the environment to construct the appropriate JMS +objects. In other words, no objects are stored in JNDI on the Apache ActiveMQ +Artemis server, instead they are simply instantiated on the client based on the +provided configuration. Let's look at the different kinds of administered +objects and how to configure them. + +> **Note:** > -> The following configuration properties *are strictly required when -> Apache ActiveMQ Artemis is running in stand-alone mode*. When Apache ActiveMQ Artemis is integrated -> to an application server (e.g. Wildfly) the application server itself -> will almost certainly provide a JNDI client with its own properties. +> The following configuration properties *are strictly required when Apache +> ActiveMQ Artemis is running in stand-alone mode*. When Apache ActiveMQ +> Artemis is integrated to an application server (e.g. Wildfly) the application +> server itself will almost certainly provide a JNDI client with its own +> properties. ### ConnectionFactory JNDI -A JMS connection factory is used by the client to make connections to -the server. It knows the location of the server it is connecting to, as -well as many other configuration parameters. +A JMS connection factory is used by the client to make connections to the +server. It knows the location of the server it is connecting to, as well as +many other configuration parameters. -Here's a simple example of the JNDI context environment for a client -looking up a connection factory to access an *embedded* instance of -Apache ActiveMQ Artemis: +Here's a simple example of the JNDI context environment for a client looking up +a connection factory to access an *embedded* instance of Apache ActiveMQ +Artemis: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.invmConnectionFactory=vm://0 -In this instance we have created a connection factory that is bound to -`invmConnectionFactory`, any entry with prefix `connectionFactory.` will - create a connection factory. +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.invmConnectionFactory=vm://0 +``` -In certain situations there could be multiple server instances running -within a particular JVM. In that situation each server would typically -have an InVM acceptor with a unique server-ID. A client using JMS and -JNDI can account for this by specifying a connction factory for each -server, like so: +In this instance we have created a connection factory that is bound to +`invmConnectionFactory`, any entry with prefix `connectionFactory.` will create +a connection factory. - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.invmConnectionFactory0=vm://0 - connectionFactory.invmConnectionFactory1=vm://1 - connectionFactory.invmConnectionFactory2=vm://2 +In certain situations there could be multiple server instances running within a +particular JVM. In that situation each server would typically have an InVM +acceptor with a unique server-ID. A client using JMS and JNDI can account for +this by specifying a connction factory for each server, like so: -Here is a list of all the supported URL schemes: -- `vm` +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.invmConnectionFactory0=vm://0 +connectionFactory.invmConnectionFactory1=vm://1 +connectionFactory.invmConnectionFactory2=vm://2 +``` -- `tcp` +Here is a list of all the supported URL schemes: -- `udp` +- `vm` +- `tcp` +- `udp` +- `jgroups` -- `jgroups` +Most clients won't be connecting to an embedded broker. Clients will most +commonly connect across a network a remote broker. Here's a simple example of a +client configuring a connection factory to connect to a remote broker running +on myhost:5445: -Most clients won't be connecting to an embedded broker. Clients will -most commonly connect across a network a remote broker. Here's a simple -example of a client configuring a connection factory to connect to a -remote broker running on myhost:5445: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - connectionFactory.ConnectionFactory=tcp://myhost:5445 +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +connectionFactory.ConnectionFactory=tcp://myhost:5445 +``` -In the example above the client is using the `tcp` scheme for the -provider URL. A client may also specify multiple comma-delimited -host:port combinations in the URL (e.g. -`(tcp://remote-host1:5445,remote-host2:5445)`). Whether there is one or -many host:port combinations in the URL they are treated as the *initial +In the example above the client is using the `tcp` scheme for the provider URL. +A client may also specify multiple comma-delimited host:port combinations in +the URL (e.g. `(tcp://remote-host1:5445,remote-host2:5445)`). Whether there is +one or many host:port combinations in the URL they are treated as the *initial connector(s)* for the underlying connection. -The `udp` scheme is also supported which should use a host:port -combination that matches the `group-address` and `group-port` from the -corresponding `broadcast-group` configured on the ActiveMQ Artemis server(s). +The `udp` scheme is also supported which should use a host:port combination +that matches the `group-address` and `group-port` from the corresponding +`broadcast-group` configured on the ActiveMQ Artemis server(s). Each scheme has a specific set of properties which can be set using the traditional URL query string format (e.g. -`scheme://host:port?key1=value1&key2=value2`) to customize the -underlying transport mechanism. For example, if a client wanted to -connect to a remote server using TCP and SSL it would create a connection -factory like so, `tcp://remote-host:5445?ssl-enabled=true`. +`scheme://host:port?key1=value1&key2=value2`) to customize the underlying +transport mechanism. For example, if a client wanted to connect to a remote +server using TCP and SSL it would create a connection factory like so, +`tcp://remote-host:5445?ssl-enabled=true`. All the properties available for the `tcp` scheme are described in [the documentation regarding the Netty transport](configuring-transports.md#configuring-the-netty-transport). -Note if you are using the `tcp` scheme and multiple addresses then a query -can be applied to all the url's or just to an individual connector, so where -you have +Note if you are using the `tcp` scheme and multiple addresses then a query can +be applied to all the url's or just to an individual connector, so where you +have -- `(tcp://remote-host1:5445?httpEnabled=true,remote-host2:5445?httpEnabled=true)?clientID=1234` +- `(tcp://remote-host1:5445?httpEnabled=true,remote-host2:5445?httpEnabled=true)?clientID=1234` -then the `httpEnabled` property is only set on the individual connectors where as the `clientId` -is set on the actual connection factory. Any connector specific properties set on the whole -URI will be applied to all the connectors. +then the `httpEnabled` property is only set on the individual connectors where +as the `clientId` is set on the actual connection factory. Any connector +specific properties set on the whole URI will be applied to all the connectors. The `udp` scheme supports 4 properties: -- `localAddress` - If you are running with multiple network - interfaces on the same machine, you may want to specify that the - discovery group listens only only a specific interface. To do this - you can specify the interface address with this parameter. - -- `localPort` - If you want to specify a local port to which the - datagram socket is bound you can specify it here. Normally you would - just use the default value of -1 which signifies that an anonymous - port should be used. This parameter is always specified in - conjunction with `localAddress`. - -- `refreshTimeout` - This is the period the discovery group waits - after receiving the last broadcast from a particular server before - removing that servers connector pair entry from its list. You would - normally set this to a value significantly higher than the - broadcast-period on the broadcast group otherwise servers might - intermittently disappear from the list even though they are still - broadcasting due to slight differences in timing. This parameter is - optional, the default value is 10000 milliseconds (10 seconds). - -- `discoveryInitialWaitTimeout` - If the connection factory is used - immediately after creation then it may not have had enough time to - received broadcasts from all the nodes in the cluster. On first - usage, the connection factory will make sure it waits this long - since creation before creating the first connection. The default - value for this parameter is 10000 milliseconds. - -Lastly, the `jgroups` scheme is supported which provides an alternative -to the `udp` scheme for server discovery. The URL pattern is either +- `localAddress` - If you are running with multiple network + interfaces on the same machine, you may want to specify that the + discovery group listens only only a specific interface. To do this + you can specify the interface address with this parameter. + +- `localPort` - If you want to specify a local port to which the + datagram socket is bound you can specify it here. Normally you would + just use the default value of -1 which signifies that an anonymous + port should be used. This parameter is always specified in + conjunction with `localAddress`. + +- `refreshTimeout` - This is the period the discovery group waits after + receiving the last broadcast from a particular server before removing that + servers connector pair entry from its list. You would normally set this to a + value significantly higher than the broadcast-period on the broadcast group + otherwise servers might intermittently disappear from the list even though they + are still broadcasting due to slight differences in timing. This parameter is + optional, the default value is 10000 milliseconds (10 seconds). + +- `discoveryInitialWaitTimeout` - If the connection factory is used immediately + after creation then it may not have had enough time to received broadcasts + from all the nodes in the cluster. On first usage, the connection factory will + make sure it waits this long since creation before creating the first + connection. The default value for this parameter is 10000 milliseconds. + +Lastly, the `jgroups` scheme is supported which provides an alternative to the +`udp` scheme for server discovery. The URL pattern is either `jgroups://channelName?file=jgroups-xml-conf-filename` -where`jgroups-xml-conf-filename` refers to an XML file on the classpath -that contains the JGroups configuration or it can be -`jgroups://channelName?properties=some-jgroups-properties`. In both instance the -`channelName` is the name given to the jgroups channel created. +where`jgroups-xml-conf-filename` refers to an XML file on the classpath that +contains the JGroups configuration or it can be +`jgroups://channelName?properties=some-jgroups-properties`. In both instance +the `channelName` is the name given to the jgroups channel created. + +The `refreshTimeout` and `discoveryInitialWaitTimeout` properties are supported +just like with `udp`. -The `refreshTimeout` and `discoveryInitialWaitTimeout` properties -are supported just like with `udp`. +The default type for the default connection factory is of type +`javax.jms.ConnectionFactory`. This can be changed by setting the type like so -The default type for the default connection factory is of type `javax.jms.ConnectionFactory`. -This can be changed by setting the type like so - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - java.naming.provider.url=tcp://localhost:5445?type=CF +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +java.naming.provider.url=tcp://localhost:5445?type=CF +``` -In this example it is still set to the default, below shows a list of types that can be set. +In this example it is still set to the default, below shows a list of types +that can be set. #### Configuration for Connection Factory Types @@ -195,42 +202,46 @@ TOPIC_XA_CF | javax.jms.XATopicConnectionFactory ### Destination JNDI -JMS destinations are also typically looked up via JNDI. As with -connection factories, destinations can be configured using special -properties in the JNDI context environment. The property *name* should -follow the pattern: `queue.` or `topic.`. -The property *value* should be the name of the queue hosted by the -Apache ActiveMQ Artemis server. For example, if the server had a JMS queue configured -like so: +JMS destinations are also typically looked up via JNDI. As with connection +factories, destinations can be configured using special properties in the JNDI +context environment. The property *name* should follow the pattern: +`queue.` or `topic.`. The property *value* should +be the name of the queue hosted by the Apache ActiveMQ Artemis server. For +example, if the server had a JMS queue configured like so: ```xml - +
+ +
``` -And if the client wanted to bind this queue to "queues/OrderQueue" then -the JNDI properties would be configured like so: +And if the client wanted to bind this queue to "queues/OrderQueue" then the +JNDI properties would be configured like so: - java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory - java.naming.provider.url=tcp://myhost:5445 - queue.queues/OrderQueue=OrderQueue +```properties +java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory +java.naming.provider.url=tcp://myhost:5445 +queue.queues/OrderQueue=OrderQueue +``` -It is also possible to look-up JMS destinations which haven't been -configured explicitly in the JNDI context environment. This is possible -using `dynamicQueues/` or `dynamicTopics/` in the look-up string. For -example, if the client wanted to look-up the aforementioned "OrderQueue" -it could do so simply by using the string "dynamicQueues/OrderQueue". -Note, the text that follows `dynamicQueues/` or `dynamicTopics/` must -correspond *exactly* to the name of the destination on the server. +It is also possible to look-up JMS destinations which haven't been configured +explicitly in the JNDI context environment. This is possible using +`dynamicQueues/` or `dynamicTopics/` in the look-up string. For example, if the +client wanted to look-up the aforementioned "OrderQueue" it could do so simply +by using the string "dynamicQueues/OrderQueue". Note, the text that follows +`dynamicQueues/` or `dynamicTopics/` must correspond *exactly* to the name of +the destination on the server. ### The code Here's the code for the example: -First we'll create a JNDI initial context from which to lookup our JMS -objects. If the above properties are set in `jndi.properties` and it is -on the classpath then any new, empty `InitialContext` will be -initialized using those properties: -```java +First we'll create a JNDI initial context from which to lookup our JMS objects. +If the above properties are set in `jndi.properties` and it is on the classpath +then any new, empty `InitialContext` will be initialized using those +properties: + +```java InitialContext ic = new InitialContext(); //Now we'll look up the connection factory from which we can create @@ -280,35 +291,36 @@ see the examples directory in the distribution. > **Warning** > -> Please note that JMS connections, sessions, producers and consumers -> are *designed to be re-used*. +> Please note that JMS connections, sessions, producers and consumers are +> *designed to be re-used*. > -> It is an anti-pattern to create new connections, sessions, producers -> and consumers for each message you produce or consume. If you do this, -> your application will perform very poorly. This is discussed further -> in the section on performance tuning [Performance Tuning](perf-tuning.md). +> It is an anti-pattern to create new connections, sessions, producers and +> consumers for each message you produce or consume. If you do this, your +> application will perform very poorly. This is discussed further in the +> section on performance tuning [Performance Tuning](perf-tuning.md). ## Directly instantiating JMS Resources without using JNDI -Although it is a very common JMS usage pattern to lookup JMS -*Administered Objects* (that's JMS Queue, Topic and ConnectionFactory -instances) from JNDI, in some cases you just think "Why do I need JNDI? -Why can't I just instantiate these objects directly?" +Although it is a very common JMS usage pattern to lookup JMS *Administered +Objects* (that's JMS Queue, Topic and ConnectionFactory instances) from JNDI, +in some cases you just think "Why do I need JNDI? Why can't I just instantiate +these objects directly?" -With Apache ActiveMQ Artemis you can do exactly that. Apache ActiveMQ Artemis supports the direct -instantiation of JMS Queue, Topic and ConnectionFactory instances, so -you don't have to use JNDI at all. +With Apache ActiveMQ Artemis you can do exactly that. Apache ActiveMQ Artemis +supports the direct instantiation of JMS Queue, Topic and ConnectionFactory +instances, so you don't have to use JNDI at all. ->For a full working example of direct instantiation please look at the ->"Instantiate JMS Objects Directly" example under the JMS section of the ->examples. See the [Examples](examples.md) section for more info. +> For a full working example of direct instantiation please look at the +> [Instantiate JMS Objects +> Directly](examples.md#instantiate-jms-objects-directly) example under the JMS +> section of the examples. Here's our simple example, rewritten to not use JNDI at all: -We create the JMS ConnectionFactory object via the ActiveMQJMSClient -Utility class, note we need to provide connection parameters and specify -which transport we are using, for more information on connectors please -see [Configuring the Transport](configuring-transports.md). +We create the JMS ConnectionFactory object via the ActiveMQJMSClient Utility +class, note we need to provide connection parameters and specify which +transport we are using, for more information on connectors please see +[Configuring the Transport](configuring-transports.md). ```java TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName()); @@ -355,35 +367,33 @@ System.out.println("Got order: " + receivedMessage.getText()); ## Setting The Client ID -This represents the client id for a JMS client and is needed for -creating durable subscriptions. It is possible to configure this on the -connection factory and can be set via the `clientId` element. Any -connection created by this connection factory will have this set as its -client id. +This represents the client id for a JMS client and is needed for creating +durable subscriptions. It is possible to configure this on the connection +factory and can be set via the `clientId` element. Any connection created by +this connection factory will have this set as its client id. ## Setting The Batch Size for DUPS_OK -When the JMS acknowledge mode is set to `DUPS_OK` it is possible to -configure the consumer so that it sends acknowledgements in batches -rather that one at a time, saving valuable bandwidth. This can be -configured via the connection factory via the `dupsOkBatchSize` -element and is set in bytes. The default is 1024 \* 1024 bytes = 1 MiB. +When the JMS acknowledge mode is set to `DUPS_OK` it is possible to configure +the consumer so that it sends acknowledgements in batches rather that one at a +time, saving valuable bandwidth. This can be configured via the connection +factory via the `dupsOkBatchSize` element and is set in bytes. The default is +1024 \* 1024 bytes = 1 MiB. ## Setting The Transaction Batch Size When receiving messages in a transaction it is possible to configure the -consumer to send acknowledgements in batches rather than individually -saving valuable bandwidth. This can be configured on the connection -factory via the `transactionBatchSize` element and is set in bytes. -The default is 1024 \* 1024. +consumer to send acknowledgements in batches rather than individually saving +valuable bandwidth. This can be configured on the connection factory via the +`transactionBatchSize` element and is set in bytes. The default is 1024 \* +1024. ## Setting The Destination Cache -Many frameworks such as Spring resolve the destination by name on every operation, -this can cause a performance issue and extra calls to the broker, -in a scenario where destinations (addresses) are permanent broker side, -such as they are managed by a platform or operations team. -using `destinationCache` element, you can toggle on the destination cache -to improve the performance and reduce the calls to the broker. -This should not be used if destinations (addresses) are not permanent broker side, -as in dynamic creation/deletion. +Many frameworks such as Spring resolve the destination by name on every +operation, this can cause a performance issue and extra calls to the broker, in +a scenario where destinations (addresses) are permanent broker side, such as +they are managed by a platform or operations team. using `destinationCache` +element, you can toggle on the destination cache to improve the performance and +reduce the calls to the broker. This should not be used if destinations +(addresses) are not permanent broker side, as in dynamic creation/deletion. diff --git a/docs/user-manual/en/using-server.md b/docs/user-manual/en/using-server.md index dea76930717..1e7b7436ae2 100644 --- a/docs/user-manual/en/using-server.md +++ b/docs/user-manual/en/using-server.md @@ -1,20 +1,22 @@ # Using the Server -This chapter will familiarise you with how to use the Apache ActiveMQ Artemis server. +This chapter will familiarise you with how to use the Apache ActiveMQ Artemis +server. We'll show where it is, how to start and stop it, and we'll describe the directory layout and what all the files are and what they do. -For the remainder of this chapter when we talk about the Apache ActiveMQ Artemis server -we mean the Apache ActiveMQ Artemis standalone server, in its default configuration -with a JMS Service enabled. +For the remainder of this chapter when we talk about the Apache ActiveMQ +Artemis server we mean the Apache ActiveMQ Artemis standalone server, in its +default configuration with a JMS Service enabled. This document will refer to the full path of the directory where the ActiveMQ distribution has been extracted to as `${ARTEMIS_HOME}` directory. ## Installation -After downloading the distribution, the following highlights some important folders on the distribution: +After downloading the distribution, the following highlights some important +folders on the distribution: |___ bin | @@ -36,48 +38,52 @@ After downloading the distribution, the following highlights some important fold |___ user-manual -- `bin` - binaries and scripts needed to run ActiveMQ Artemis. +- `bin` - binaries and scripts needed to run ActiveMQ Artemis. -- `examples` - All manner of examples. Please refer to the [examples](examples.md) - chapter for details on how to run them. +- `examples` - All manner of examples. Please refer to the [examples](examples.md) + chapter for details on how to run them. -- `lib` - jars and libraries needed to run ActiveMQ Artemis +- `lib` - jars and libraries needed to run ActiveMQ Artemis -- `schema` - XML Schemas used to validate ActiveMQ Artemis configuration files +- `schema` - XML Schemas used to validate ActiveMQ Artemis configuration files -- `web` - The folder where the web context is loaded when the broker runs. +- `web` - The folder where the web context is loaded when the broker runs. -- `api` - The api documentation is placed under the web folder. +- `api` - The api documentation is placed under the web folder. -- `user-manual` - The user manual is placed under the web folder. +- `user-manual` - The user manual is placed under the web folder. ## Creating a Broker Instance A broker instance is the directory containing all the configuration and runtime -data, such as logs and data files, associated with a broker process. It is recommended that -you do *not* create the instance directory under `${ARTEMIS_HOME}`. This separation is -encouraged so that you can more easily upgrade when the next version of ActiveMQ Artemis is released. - -On Unix systems, it is a common convention to store this kind of runtime data under -the `/var/lib` directory. For example, to create an instance at '/var/lib/mybroker', run -the following commands in your command line shell: - - cd /var/lib - ${ARTEMIS_HOME}/bin/artemis create mybroker +data, such as logs and data files, associated with a broker process. It is +recommended that you do *not* create the instance directory under +`${ARTEMIS_HOME}`. This separation is encouraged so that you can more easily +upgrade when the next version of ActiveMQ Artemis is released. + +On Unix systems, it is a common convention to store this kind of runtime data +under the `/var/lib` directory. For example, to create an instance at +'/var/lib/mybroker', run the following commands in your command line shell: + +```sh +cd /var/lib +${ARTEMIS_HOME}/bin/artemis create mybroker +``` A broker instance directory will contain the following sub directories: - * `bin`: holds execution scripts associated with this instance. - * `etc`: hold the instance configuration files - * `data`: holds the data files used for storing persistent messages - * `log`: holds rotating log files - * `tmp`: holds temporary files that are safe to delete between broker runs +- `bin`: holds execution scripts associated with this instance. +- `etc`: hold the instance configuration files +- `data`: holds the data files used for storing persistent messages +- `log`: holds rotating log files +- `tmp`: holds temporary files that are safe to delete between broker runs -At this point you may want to adjust the default configuration located in -the `etc` directory. +At this point you may want to adjust the default configuration located in the +`etc` directory. ### Options + There are several options you can use when creating an instance. For a full list of updated properties always use: @@ -288,35 +294,35 @@ For a full list of updated properties always use: Path must be writable. ``` - -Some of these properties may be mandatory in certain configurations and the system may ask you for additional input. +Some of these properties may be mandatory in certain configurations and the +system may ask you for additional input. ``` - ./artemis create /usr/server - Creating ActiveMQ Artemis instance at: /user/server +./artemis create /usr/server +Creating ActiveMQ Artemis instance at: /user/server - --user: is a mandatory property! - Please provide the default username: - admin +--user: is a mandatory property! +Please provide the default username: +admin - --password: is mandatory with this configuration: - Please provide the default password: +--password: is mandatory with this configuration: +Please provide the default password: - --allow-anonymous | --require-login: is a mandatory property! - Allow anonymous access?, valid values are Y,N,True,False - y +--allow-anonymous | --require-login: is a mandatory property! +Allow anonymous access?, valid values are Y,N,True,False +y - Auto tuning journal ... - done! Your system can make 0.34 writes per millisecond, your journal-buffer-timeout will be 2956000 +Auto tuning journal ... +done! Your system can make 0.34 writes per millisecond, your journal-buffer-timeout will be 2956000 - You can now start the broker by executing: +You can now start the broker by executing: - "/user/server/bin/artemis" run + "/user/server/bin/artemis" run - Or you can run the broker in the background using: +Or you can run the broker in the background using: - "/user/server/bin/artemis-service" start + "/user/server/bin/artemis-service" start ``` @@ -325,22 +331,26 @@ Some of these properties may be mandatory in certain configurations and the syst Assuming you created the broker instance under `/var/lib/mybroker` all you need to do start running the broker instance is execute: - /var/lib/mybroker/bin/artemis run +```sh +/var/lib/mybroker/bin/artemis run +``` Now that the broker is running, you can optionally run some of the included examples to verify the the broker is running properly. -To stop the Apache ActiveMQ Artemis instance you will use the same `artemis` script, but with -the `stop argument`. Example: +To stop the Apache ActiveMQ Artemis instance you will use the same `artemis` +script, but with the `stop` argument. Example: - /var/lib/mybroker/bin/artemis stop +```sh +/var/lib/mybroker/bin/artemis stop +``` -Please note that Apache ActiveMQ Artemis requires a Java 7 or later runtime to run. +Please note that Apache ActiveMQ Artemis requires a Java 7 or later runtime to +run. -By default the `etc/bootstrap.xml` configuration is -used. The configuration can be changed e.g. by running -`./artemis run -- xml:path/to/bootstrap.xml` or another -config of your choosing. +By default the `etc/bootstrap.xml` configuration is used. The configuration can +be changed e.g. by running `./artemis run -- xml:path/to/bootstrap.xml` or +another config of your choosing. Environment variables are used to provide ease of changing ports, hosts and data directories used and can be found in `etc/artemis.profile` on linux and @@ -348,65 +358,64 @@ data directories used and can be found in `etc/artemis.profile` on linux and ## Server JVM settings -The run scripts set some JVM settings for tuning the garbage collection -policy and heap size. We recommend using a parallel garbage collection -algorithm to smooth out latency and minimise large GC pauses. +The run scripts set some JVM settings for tuning the garbage collection policy +and heap size. We recommend using a parallel garbage collection algorithm to +smooth out latency and minimise large GC pauses. -By default Apache ActiveMQ Artemis runs in a maximum of 1GiB of RAM. To increase the -memory settings change the `-Xms` and `-Xmx` memory settings as you -would for any Java program. +By default Apache ActiveMQ Artemis runs in a maximum of 1GiB of RAM. To +increase the memory settings change the `-Xms` and `-Xmx` memory settings as +you would for any Java program. -If you wish to add any more JVM arguments or tune the existing ones, the -run scripts are the place to do it. +If you wish to add any more JVM arguments or tune the existing ones, the run +scripts are the place to do it. ## Library Path -If you're using the [Asynchronous IO Journal](libaio.md) on Linux, -you need to specify `java.library.path` as a property on your Java -options. This is done automatically in the scripts. +If you're using the [Asynchronous IO Journal](libaio.md) on Linux, you need to +specify `java.library.path` as a property on your Java options. This is done +automatically in the scripts. -If you don't specify `java.library.path` at your Java options then the -JVM will use the environment variable `LD_LIBRARY_PATH`. +If you don't specify `java.library.path` at your Java options then the JVM will +use the environment variable `LD_LIBRARY_PATH`. -You will need to make sure libaio is installed on Linux. For more information refer to the libaio chapter at -[Runtime Dependencies](libaio.html#runtime-dependencies) +You will need to make sure libaio is installed on Linux. For more information +refer to the [libaio chapter](libaio.html#runtime-dependencies). ## System properties -Apache ActiveMQ Artemis can take a system property on the command line for configuring -logging. +Apache ActiveMQ Artemis can take a system property on the command line for +configuring logging. For more information on configuring logging, please see the section on [Logging](logging.md). ## Configuration files -The configuration file used to bootstrap the server (e.g. -`bootstrap.xml` by default) references the specific broker configuration -files. +The configuration file used to bootstrap the server (e.g. `bootstrap.xml` by +default) references the specific broker configuration files. -- `broker.xml`. This is the main ActiveMQ - configuration file. All the parameters in this file are - described [here](configuration-index.md) +- `broker.xml`. This is the main ActiveMQ configuration file. All the + parameters in this file are described [here](configuration-index.md) It is also possible to use system property substitution in all the -configuration files. by replacing a value with the name of a system -property. Here is an example of this with a connector configuration: +configuration files. by replacing a value with the name of a system property. +Here is an example of this with a connector configuration: - tcp://${activemq.remoting.netty.host:localhost}:${activemq.remoting.netty.port:61616} +```xml +tcp://${activemq.remoting.netty.host:localhost}:${activemq.remoting.netty.port:61616} +``` Here you can see we have replaced 2 values with system properties -`activemq.remoting.netty.host` and `activemq.remoting.netty.port`. These -values will be replaced by the value found in the system property if -there is one, if not they default back to localhost or 61616 -respectively. It is also possible to not supply a default. i.e. -`${activemq.remoting.netty.host}`, however the system property *must* be -supplied in that case. +`activemq.remoting.netty.host` and `activemq.remoting.netty.port`. These values +will be replaced by the value found in the system property if there is one, if +not they default back to localhost or 61616 respectively. It is also possible +to not supply a default. i.e. `${activemq.remoting.netty.host}`, however the +system property *must* be supplied in that case. ### Bootstrap configuration file -The stand-alone server is basically a set of POJOs which are -instantiated by Airline commands. +The stand-alone server is basically a set of POJOs which are instantiated by +Airline commands. The bootstrap file is very simple. Let's take a look at an example: @@ -425,48 +434,47 @@ The bootstrap file is very simple. Let's take a look at an example: ``` +- `server` - Instantiates a core server using the configuration file from the + `configuration` attribute. This is the main broker POJO necessary to do all + the real messaging work. -- `server` - Instantiates a core server using the configuration file from the - `configuration` attribute. This is the main broker POJO necessary to - do all the real messaging work. +- `jaas-security` - Configures security for the server. The `domain` attribute + refers to the relevant login module entry in `login.config`. -- `jaas-security` - Configures security for the server. The `domain` attribute - refers to the relevant login module entry in `login.config`. - -- `web` - Configures an embedded Jetty instance to serve web applications like - the admin console. +- `web` - Configures an embedded Jetty instance to serve web applications like + the admin console. ### Broker configuration file The configuration for the Apache ActiveMQ Artemis core server is contained in -`broker.xml`. This is what the FileConfiguration bean -uses to configure the messaging server. +`broker.xml`. This is what the FileConfiguration bean uses to configure the +messaging server. -There are many attributes which you can configure Apache ActiveMQ Artemis. In most -cases the defaults will do fine, in fact every attribute can be -defaulted which means a file with a single empty `configuration` element -is a valid configuration file. The different configuration will be -explained throughout the manual or you can refer to the configuration -reference [here](configuration-index.md). +There are many attributes which you can configure Apache ActiveMQ Artemis. In +most cases the defaults will do fine, in fact every attribute can be defaulted +which means a file with a single empty `configuration` element is a valid +configuration file. The different configuration will be explained throughout +the manual or you can refer to the configuration reference +[here](configuration-index.md). ## Windows Server -On windows you will have the option to run ActiveMQ Artemis as a service. -Just use the following command to install it: +On windows you will have the option to run ActiveMQ Artemis as a service. Just +use the following command to install it: ``` $ ./artemis-service.exe install ``` - -The create process should give you a hint of the available commands available for the artemis-service.exe +The create process should give you a hint of the available commands available +for the artemis-service.exe ## Adding Runtime Dependencies Runtime dependencies like diverts, transformers, broker plugins, JDBC drivers, -password decoders, etc. must be accessible by the broker at runtime. Package +password decoders, etc. must be accessible by the broker at runtime. Package the dependency in a jar, and put it on the broker's classpath. This can be done -by placing the jar file in the `lib` directory of the broker distribution itself -or in the `lib` directory of the broker instance. A broker instance does not have -a `lib` directory by default so it may need to be created. It should be on the -"top" level with the `bin`, `data`, `log`, etc. directories. \ No newline at end of file +by placing the jar file in the `lib` directory of the broker distribution +itself or in the `lib` directory of the broker instance. A broker instance does +not have a `lib` directory by default so it may need to be created. It should +be on the "top" level with the `bin`, `data`, `log`, etc. directories. diff --git a/docs/user-manual/en/versions.md b/docs/user-manual/en/versions.md index 526fa30efba..9bb33d6387e 100644 --- a/docs/user-manual/en/versions.md +++ b/docs/user-manual/en/versions.md @@ -4,9 +4,9 @@ This chapter provides the information for each release: - A link to the full release notes which includes all issues resolved in the release. - A brief list of "highlights." - If necessary, specific steps required when upgrading from the previous version. - - _NOTE:_ If the upgrade spans multiple versions then the steps from each version need to be followed in order. - - _NOTE:_ Follow the general upgrade procedure outlined in the [Upgrading the Broker](upgrading.md) - chapter in addition to any version-specific upgrade instructions. + - **Note:** If the upgrade spans multiple versions then the steps from **each** version need to be followed in order. + - **Note:** Follow the general upgrade procedure outlined in the [Upgrading the Broker](upgrading.md) + chapter in addition to any version-specific upgrade instructions outlined here. ## 2.5.0 @@ -58,7 +58,7 @@ Highlights: ```xml ``` - _NOTE:_ the Jolokia REST interface URL will now be at `http://:/console/jolokia` + **Note:** the Jolokia REST interface URL will now be at `http://:/console/jolokia` ## 2.3.0 diff --git a/docs/user-manual/en/wildcard-routing.md b/docs/user-manual/en/wildcard-routing.md index 96540f35684..0db77485432 100644 --- a/docs/user-manual/en/wildcard-routing.md +++ b/docs/user-manual/en/wildcard-routing.md @@ -8,7 +8,7 @@ receive any messages sent to addresses that match this, for instance you create a consumer on this queue, this allows a consumer to consume messages which are sent to a *hierarchy* of addresses. -> **Note** +> **Note:** > > In JMS terminology this allows "topic hierarchies" to be created. diff --git a/examples/features/standard/spring-integration/src/main/resources/broker.xml b/examples/features/standard/spring-integration/src/main/resources/broker.xml index 9d703b3ce99..505b37fee79 100644 --- a/examples/features/standard/spring-integration/src/main/resources/broker.xml +++ b/examples/features/standard/spring-integration/src/main/resources/broker.xml @@ -26,11 +26,10 @@ under the License. vm://0 - - - + + @@ -39,13 +38,5 @@ under the License. - - -
- - - -
-
diff --git a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml index 7db39ea9bca..07545fecac2 100644 --- a/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml +++ b/examples/features/standard/spring-integration/src/main/resources/spring-jms-beans.xml @@ -49,16 +49,11 @@ under the License. - + - - - - - - +