diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ParameterisedAddress.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ParameterisedAddress.java new file mode 100644 index 0000000000..6a6d45c81c --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/ParameterisedAddress.java @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.api.core; + +import static org.apache.activemq.artemis.utils.uri.URISupport.appendParameters; +import static org.apache.activemq.artemis.utils.uri.URISupport.parseQuery; + +import java.net.URISyntaxException; +import java.util.Map; + +import org.apache.activemq.artemis.utils.uri.URISupport; + +public class ParameterisedAddress { + + public static SimpleString toParameterisedAddress(SimpleString address, Map parameters) throws URISyntaxException { + if (parameters != null && !parameters.isEmpty()) { + return SimpleString.toSimpleString(toParameterisedAddress(address.toString(), parameters)); + } else { + return address; + } + } + + public static String toParameterisedAddress(String address, Map parameters) throws URISyntaxException { + if (parameters != null && !parameters.isEmpty()) { + return appendParameters(new StringBuilder(address), parameters).toString(); + } else { + return address; + } + } + + private final SimpleString address; + private final QueueAttributes queueAttributes; + + public SimpleString getAddress() { + return address; + } + + public QueueAttributes getQueueAttributes() { + return queueAttributes; + } + + public ParameterisedAddress(SimpleString address, QueueAttributes queueAttributes) { + this.address = address; + this.queueAttributes = queueAttributes; + } + + public ParameterisedAddress(String address, QueueAttributes queueAttributes) { + this(SimpleString.toSimpleString(address), queueAttributes); + } + + public ParameterisedAddress(SimpleString address) { + this(address.toString()); + } + + public ParameterisedAddress(String address) { + int index = address.indexOf('?'); + if (index == -1) { + this.address = SimpleString.toSimpleString(address); + this.queueAttributes = null; + } else { + this.address = SimpleString.toSimpleString(address.substring(0, index)); + QueueAttributes queueAttributes = new QueueAttributes(); + try { + parseQuery(address).forEach(queueAttributes::set); + } catch (URISyntaxException use) { + throw new IllegalArgumentException("Malformed parameters in address " + address); + } + this.queueAttributes = queueAttributes; + } + } + + public boolean isParameterised() { + return this.queueAttributes != null; + } + + public static boolean isParameterised(String address) { + return URISupport.containsQuery(address); + } + + public static boolean isParameterised(SimpleString address) { + return URISupport.containsQuery(address); + } + +} diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/QueueAttributes.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/QueueAttributes.java new file mode 100644 index 0000000000..6a43b39cfa --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/api/core/QueueAttributes.java @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.activemq.artemis.api.core; + +import java.io.Serializable; + +public class QueueAttributes implements Serializable { + + public static final String MAX_CONSUMERS = "max-consumers"; + public static final String EXCLUSIVE = "exclusive"; + public static final String LAST_VALUE = "last-value"; + public static final String PURGE_ON_NO_CONSUMERS = "purge-on-no-consumers"; + + private Integer maxConsumers; + private Boolean exclusive; + private Boolean lastValue; + private Boolean purgeOnNoConsumers; + + public void set(String key, String value) { + if (key != null && value != null) { + if (key.equals(MAX_CONSUMERS)) { + setMaxConsumers(Integer.valueOf(value)); + } else if (key.equals(EXCLUSIVE)) { + setExclusive(Boolean.valueOf(value)); + } else if (key.equals(LAST_VALUE)) { + setLastValue(Boolean.valueOf(value)); + } else if (key.equals(PURGE_ON_NO_CONSUMERS)) { + setPurgeOnNoConsumers(Boolean.valueOf(value)); + } + } + } + + public Integer getMaxConsumers() { + return maxConsumers; + } + + public void setMaxConsumers(Integer maxConsumers) { + this.maxConsumers = maxConsumers; + } + + public Boolean getExclusive() { + return exclusive; + } + + public void setExclusive(Boolean exclusive) { + this.exclusive = exclusive; + } + + public Boolean getLastValue() { + return lastValue; + } + + public void setLastValue(Boolean lastValue) { + this.lastValue = lastValue; + } + + public Boolean getPurgeOnNoConsumers() { + return purgeOnNoConsumers; + } + + public void setPurgeOnNoConsumers(Boolean purgeOnNoConsumers) { + this.purgeOnNoConsumers = purgeOnNoConsumers; + } +} diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java index e26993d29d..7530759a5a 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/uri/URISupport.java @@ -27,6 +27,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.activemq.artemis.api.core.SimpleString; + /** * Utility class that provides methods for parsing URI's * @@ -75,7 +77,7 @@ public class URISupport { } public URI toURI() throws URISyntaxException { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); if (scheme != null) { sb.append(scheme); sb.append(':'); @@ -98,18 +100,23 @@ public class URISupport { sb.append('/'); sb.append(path); } - if (!parameters.isEmpty()) { - sb.append("?"); - sb.append(createQueryString(parameters)); - } + appendParameters(sb, parameters); if (fragment != null) { - sb.append("#"); + sb.append('#'); sb.append(fragment); } return new URI(sb.toString()); } } + public static StringBuilder appendParameters(StringBuilder sb, Map parameters) throws URISyntaxException { + if (!parameters.isEmpty()) { + sb.append('?'); + sb.append(createQueryString(parameters)); + } + return sb; + } + /** * Give a URI break off any URI options and store them in a Key / Value Mapping. * @@ -122,8 +129,7 @@ public class URISupport { uri = uri.substring(uri.lastIndexOf("?") + 1); // get only the relevant part of the query Map rc = new HashMap<>(); if (uri != null && !uri.isEmpty()) { - parseParameters(rc, uri.split("&")); - parseParameters(rc, uri.split(";")); + parseParameters(rc, uri.split("[&;]")); } return rc; } catch (UnsupportedEncodingException e) { @@ -131,6 +137,14 @@ public class URISupport { } } + public static boolean containsQuery(String uri) { + return uri.contains("?"); + } + + public static boolean containsQuery(SimpleString uri) { + return uri.contains('?'); + } + private static void parseParameters(Map rc, String[] parameters) throws UnsupportedEncodingException { for (String parameter : parameters) { @@ -198,7 +212,7 @@ public class URISupport { Map queryParameters, String optionPrefix) throws URISyntaxException { if (queryParameters != null && !queryParameters.isEmpty()) { - StringBuffer newQuery = uri.getRawQuery() != null ? new StringBuffer(uri.getRawQuery()) : new StringBuffer(); + StringBuilder newQuery = uri.getRawQuery() != null ? new StringBuilder(uri.getRawQuery()) : new StringBuilder(); for (Map.Entry param : queryParameters.entrySet()) { if (param.getKey().startsWith(optionPrefix)) { if (newQuery.length() != 0) { diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index fe7e1bf1c0..87f6dad9f1 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -465,6 +465,10 @@ public final class ActiveMQDefaultConfiguration { public static final int DEFAULT_MAX_QUEUE_CONSUMERS = -1; + public static final boolean DEFAULT_EXCLUSIVE = false; + + public static final boolean DEFAULT_LAST_VALUE = false; + public static final boolean DEFAULT_PURGE_ON_NO_CONSUMERS = false; public static final RoutingType DEFAULT_ROUTING_TYPE = RoutingType.MULTICAST; @@ -1277,6 +1281,14 @@ public final class ActiveMQDefaultConfiguration { return DEFAULT_MAX_QUEUE_CONSUMERS; } + public static boolean getDefaultExclusive() { + return DEFAULT_EXCLUSIVE; + } + + public static boolean getDefaultLastValue() { + return DEFAULT_EXCLUSIVE; + } + public static boolean getDefaultPurgeOnNoConsumers() { return DEFAULT_PURGE_ON_NO_CONSUMERS; } 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 ce56013ade..ddc8168758 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 @@ -75,6 +75,10 @@ public interface ClientSession extends XAResource, AutoCloseable { boolean isDefaultPurgeOnNoConsumers(); int getDefaultMaxConsumers(); + + Boolean isDefaultLastValueQueue(); + + Boolean isDefaultExclusive(); } /** @@ -139,6 +143,10 @@ public interface ClientSession extends XAResource, AutoCloseable { boolean isPurgeOnNoConsumers(); boolean isAutoCreated(); + + Boolean isExclusive(); + + Boolean isLastValue(); } // Lifecycle operations ------------------------------------------ @@ -454,6 +462,23 @@ public interface ClientSession extends XAResource, AutoCloseable { void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable) throws ActiveMQException; + /** + * 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 queueName the name of the queue + * @param filter whether the queue is durable or not + * @param durable if the queue is durable + * @param maxConsumers how many concurrent consumers will be allowed on this queue + * @param purgeOnNoConsumers whether to delete the contents of the queue when the last consumer disconnects + * @param exclusive if the queue is exclusive queue + * @param lastValue if the queue is last value queue + * @throws ActiveMQException in an exception occurs while creating the queue + */ + void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + boolean durable, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException; + /** * Creates a non-temporary queue. * @@ -540,6 +565,24 @@ public interface ClientSession extends XAResource, AutoCloseable { void createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers) throws ActiveMQException; + /** + * 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 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 + * @param autoCreated whether to mark this queue as autoCreated or not + * @param maxConsumers how many concurrent consumers will be allowed on this queue + * @param purgeOnNoConsumers whether to delete the contents of the queue when the last consumer disconnects + * @param exclusive whether the queue should be exclusive + * @param lastValue whether the queue should be lastValue + * @throws ActiveMQException + */ + void createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + boolean durable, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException; + /** * Creates a non-temporaryqueue. * @@ -569,6 +612,24 @@ public interface ClientSession extends XAResource, AutoCloseable { void createQueue(String address, RoutingType routingType, String queueName, String filter, boolean durable, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers) throws ActiveMQException; + /** + * 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 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 + * @param autoCreated whether to mark this queue as autoCreated or not + * @param maxConsumers how many concurrent consumers will be allowed on this queue + * @param purgeOnNoConsumers whether to delete the contents of the queue when the last consumer disconnects + * @param exclusive whether the queue should be exclusive + * @param lastValue whether the queue should be lastValue + * @throws ActiveMQException + */ + void createQueue(String address, RoutingType routingType, String queueName, String filter, boolean durable, boolean autoCreated, + int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException; + /** * Creates a temporary queue. * @@ -589,6 +650,22 @@ public interface ClientSession extends XAResource, AutoCloseable { */ void createTemporaryQueue(String address, RoutingType routingType, String queueName) throws ActiveMQException; + /** + * 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 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 + * @param purgeOnNoConsumers whether to delete the contents of the queue when the last consumer disconnects + * @param exclusive if the queue is exclusive queue + * @param lastValue if the queue is last value queue + * @throws ActiveMQException in an exception occurs while creating the queue + */ + void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, int maxConsumers, + boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException; + /** * Creates a temporary queue with a filter. * diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index 7c9a40aca4..fc9de24c2d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -617,6 +617,22 @@ public interface ActiveMQServerControl { @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers) throws Exception; + /** + * Update a queue. + * + * @param name name of the queue + * @param routingType the routing type used for this address, {@code MULTICAST} or {@code ANYCAST} + * @param maxConsumers the maximum number of consumers allowed on this queue at any one time + * @param purgeOnNoConsumers delete this queue when the last consumer disconnects + * @return a textual summary of the queue + * @throws Exception + */ + String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive) throws Exception; + /** * Deploy a durable queue. diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java index 9eec8e0130..447417f372 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/QueueControl.java @@ -164,6 +164,18 @@ public interface QueueControl { @Attribute(desc = "delete this queue when the last consumer disconnects") boolean isPurgeOnNoConsumers(); + /** + * + */ + @Attribute(desc = "If the queue should route exclusively to one consumer") + boolean isExclusive(); + + /** + * + */ + @Attribute(desc = "is this queue a last value queue") + boolean isLastValue(); + // Operations ---------------------------------------------------- /** diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/AddressQueryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/AddressQueryImpl.java index 79e2cc92a6..cb91919405 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/AddressQueryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/AddressQueryImpl.java @@ -36,18 +36,26 @@ public class AddressQueryImpl implements ClientSession.AddressQuery { private final int defaultMaxConsumers; + private final Boolean defaultExclusive; + + private final Boolean defaultLastValue; + public AddressQueryImpl(final boolean exists, final List queueNames, final boolean autoCreateQueues, final boolean autoCreateAddresses, final boolean defaultPurgeOnNoConsumers, - final int defaultMaxConsumers) { + final int defaultMaxConsumers, + final Boolean defaultExclusive, + final Boolean defaultLastValue) { this.exists = exists; this.queueNames = new ArrayList<>(queueNames); this.autoCreateQueues = autoCreateQueues; this.autoCreateAddresses = autoCreateAddresses; this.defaultPurgeOnNoConsumers = defaultPurgeOnNoConsumers; this.defaultMaxConsumers = defaultMaxConsumers; + this.defaultExclusive = defaultExclusive; + this.defaultLastValue = defaultLastValue; } @Override @@ -79,4 +87,14 @@ public class AddressQueryImpl implements ClientSession.AddressQuery { public int getDefaultMaxConsumers() { return defaultMaxConsumers; } + + @Override + public Boolean isDefaultLastValueQueue() { + return defaultLastValue; + } + + @Override + public Boolean isDefaultExclusive() { + return defaultExclusive; + } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java index e5e91dbd99..ab9888e452 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionImpl.java @@ -376,7 +376,7 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - autoCreated); + autoCreated, null, null); } @Override @@ -400,12 +400,42 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, maxConsumers, purgeOnNoConsumers, - autoCreated); + autoCreated, null, null); + } + + @Override + public void createQueue(final SimpleString address, final RoutingType routingType, final SimpleString queueName, final SimpleString filterString, + final boolean durable, final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers, final Boolean exclusive, final Boolean lastValue) throws ActiveMQException { + internalCreateQueue(address, + queueName, routingType, + filterString, + durable, + false, + maxConsumers, + purgeOnNoConsumers, + autoCreated, + exclusive, + lastValue); } @Override public void createQueue(final String address, final RoutingType routingType, final String queueName, final String filterString, final boolean durable, final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers) throws ActiveMQException { + createQueue(address, + routingType, + queueName, + filterString, + durable, + autoCreated, + maxConsumers, + purgeOnNoConsumers, + null, + null); + } + + @Override + public void createQueue(final String address, final RoutingType routingType, final String queueName, final String filterString, + final boolean durable, final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { createQueue(SimpleString.toSimpleString(address), routingType, SimpleString.toSimpleString(queueName), @@ -413,7 +443,9 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi durable, autoCreated, maxConsumers, - purgeOnNoConsumers); + purgeOnNoConsumers, + exclusive, + lastValue); } @Override @@ -432,15 +464,27 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi public void createTemporaryQueue(final SimpleString address, final RoutingType routingType, final SimpleString queueName, - final SimpleString filter) throws ActiveMQException { + final SimpleString filter, + final int maxConsumers, + final boolean purgeOnNoConsumers, + final Boolean exclusive, + final Boolean lastValue) throws ActiveMQException { internalCreateQueue(address, queueName, routingType, filter, false, true, - ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), - ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - false); + maxConsumers, + purgeOnNoConsumers, + false, exclusive, lastValue); + } + + @Override + public void createTemporaryQueue(final SimpleString address, + final RoutingType routingType, + final SimpleString queueName, + final SimpleString filter) throws ActiveMQException { + createTemporaryQueue(address, routingType, queueName, filter, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), null, null); } @Override @@ -466,7 +510,7 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - false); + false, null, null); } /** @@ -500,11 +544,31 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi @Override public void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable) throws ActiveMQException { + createSharedQueue(address, routingType, queueName, filter, durable, null, null, null, null); + } + + /** + * 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 queueName the name of the queue + * @param filter whether the queue is durable or not + * @param durable if the queue is durable + * @param maxConsumers how many concurrent consumers will be allowed on this queue + * @param purgeOnNoConsumers whether to delete the contents of the queue when the last consumer disconnects + * @param exclusive if the queue is exclusive queue + * @param lastValue if the queue is last value queue + * @throws ActiveMQException in an exception occurs while creating the queue + */ + @Override + public void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + boolean durable, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { checkClosed(); startCall(); try { - sessionContext.createSharedQueue(address, queueName, routingType, filter, durable); + sessionContext.createSharedQueue(address, queueName, routingType, filter, durable, maxConsumers, purgeOnNoConsumers, exclusive, lastValue); } finally { endCall(); } @@ -541,7 +605,7 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - false); + false, null, null); } /** @@ -562,7 +626,7 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - false); + false, null, null); } /** @@ -586,7 +650,7 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi false, ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), - false); + false, null, null); } /** @@ -1847,7 +1911,9 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi final boolean temp, final int maxConsumers, final boolean purgeOnNoConsumers, - final boolean autoCreated) throws ActiveMQException { + final boolean autoCreated, + final Boolean exclusive, + final Boolean lastValue) throws ActiveMQException { checkClosed(); if (durable && temp) { @@ -1864,7 +1930,9 @@ public final class ClientSessionImpl implements ClientSessionInternal, FailureLi temp, maxConsumers, purgeOnNoConsumers, - autoCreated); + autoCreated, + exclusive, + lastValue); } finally { endCall(); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java index 71488c2868..8dc35a90f3 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/QueueQueryImpl.java @@ -48,6 +48,10 @@ public class QueueQueryImpl implements ClientSession.QueueQuery { private final int maxConsumers; + private final Boolean exclusive; + + private final Boolean lastValue; + public QueueQueryImpl(final boolean durable, final boolean temporary, final int consumerCount, @@ -84,6 +88,23 @@ public class QueueQueryImpl implements ClientSession.QueueQuery { final boolean autoCreated, final boolean purgeOnNoConsumers, final RoutingType routingType) { + this(durable, temporary, consumerCount, messageCount, filterString, address, name, exists, autoCreateQueues, maxConsumers, autoCreated, purgeOnNoConsumers, routingType, null, null); + } + public QueueQueryImpl(final boolean durable, + final boolean temporary, + final int consumerCount, + final long messageCount, + final SimpleString filterString, + final SimpleString address, + final SimpleString name, + final boolean exists, + final boolean autoCreateQueues, + final int maxConsumers, + final boolean autoCreated, + final boolean purgeOnNoConsumers, + final RoutingType routingType, + final Boolean exclusive, + final Boolean lastValue) { this.durable = durable; this.temporary = temporary; this.consumerCount = consumerCount; @@ -97,6 +118,8 @@ public class QueueQueryImpl implements ClientSession.QueueQuery { this.autoCreated = autoCreated; this.purgeOnNoConsumers = purgeOnNoConsumers; this.routingType = routingType; + this.exclusive = exclusive; + this.lastValue = lastValue; } @Override @@ -164,5 +187,15 @@ public class QueueQueryImpl implements ClientSession.QueueQuery { return autoCreated; } + @Override + public Boolean isExclusive() { + return exclusive; + } + + @Override + public Boolean isLastValue() { + return lastValue; + } + } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java index 3a4878b26d..6037925e3d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/ActiveMQSessionContext.java @@ -244,13 +244,26 @@ public class ActiveMQSessionContext extends SessionContext { this.sendAckHandler = handler; } + @Override + public void createSharedQueue(SimpleString address, + SimpleString queueName, + RoutingType routingType, + SimpleString filterString, + boolean durable, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Boolean lastValue) throws ActiveMQException { + sessionChannel.sendBlocking(new CreateSharedQueueMessage_V2(address, queueName, routingType, filterString, durable, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, true), PacketImpl.NULL_RESPONSE); + } + @Override public void createSharedQueue(SimpleString address, SimpleString queueName, RoutingType routingType, SimpleString filterString, boolean durable) throws ActiveMQException { - sessionChannel.sendBlocking(new CreateSharedQueueMessage_V2(address, queueName, routingType, filterString, durable, true), PacketImpl.NULL_RESPONSE); + createSharedQueue(address, queueName, routingType, filterString, durable, null, null, null, null); } @Override @@ -325,19 +338,19 @@ public class ActiveMQSessionContext extends SessionContext { if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V4, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V4); SessionBindingQueryResponseMessage_V4 response = (SessionBindingQueryResponseMessage_V4) packet; - return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), response.isDefaultPurgeOnNoConsumers(), response.getDefaultMaxConsumers()); + return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), response.isDefaultPurgeOnNoConsumers(), response.getDefaultMaxConsumers(), response.isDefaultExclusive(), response.isDefaultLastValue()); } else if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V3, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V3); SessionBindingQueryResponseMessage_V3 response = (SessionBindingQueryResponseMessage_V3) packet; - return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); + return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), response.isAutoCreateAddresses(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), null, null); } else if (sessionChannel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V2, getServerVersion())) { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP_V2); SessionBindingQueryResponseMessage_V2 response = (SessionBindingQueryResponseMessage_V2) packet; - return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); + return new AddressQueryImpl(response.isExists(), response.getQueueNames(), response.isAutoCreateQueues(), false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), null, null); } else { Packet packet = sessionChannel.sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP); SessionBindingQueryResponseMessage response = (SessionBindingQueryResponseMessage) packet; - return new AddressQueryImpl(response.isExists(), response.getQueueNames(), false, false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); + return new AddressQueryImpl(response.isExists(), response.getQueueNames(), false, false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), null, null); } } @@ -647,16 +660,32 @@ public class ActiveMQSessionContext extends SessionContext { boolean temp, int maxConsumers, boolean purgeOnNoConsumers, - boolean autoCreated) throws ActiveMQException { + boolean autoCreated, + Boolean exclusive, + Boolean lastValue) throws ActiveMQException { if (sessionChannel.getConnection().isVersionBeforeAddressChange()) { CreateQueueMessage request = new CreateQueueMessage(address, queueName, filterString, durable, temp, true); sessionChannel.sendBlocking(request, PacketImpl.NULL_RESPONSE); } else { - CreateQueueMessage request = new CreateQueueMessage_V2(address, queueName, routingType, filterString, durable, temp, maxConsumers, purgeOnNoConsumers, autoCreated, true); + CreateQueueMessage request = new CreateQueueMessage_V2(address, queueName, routingType, filterString, durable, temp, maxConsumers, purgeOnNoConsumers, autoCreated, true, exclusive, lastValue); sessionChannel.sendBlocking(request, PacketImpl.NULL_RESPONSE); } } + @Deprecated + @Override + public void createQueue(SimpleString address, + RoutingType routingType, + SimpleString queueName, + SimpleString filterString, + boolean durable, + boolean temp, + int maxConsumers, + boolean purgeOnNoConsumers, + boolean autoCreated) throws ActiveMQException { + createQueue(address, routingType, queueName, filterString, durable, temp, maxConsumers, purgeOnNoConsumers, autoCreated, null, null); + } + @Override public boolean reattachOnNewConnection(RemotingConnection newConnection) throws ActiveMQException { @@ -741,7 +770,7 @@ public class ActiveMQSessionContext extends SessionContext { // they are defined in broker.xml // This allows e.g. JMS non durable subs and temporary queues to continue to be used after failover if (!queueInfo.isDurable()) { - CreateQueueMessage_V2 createQueueRequest = new CreateQueueMessage_V2(queueInfo.getAddress(), queueInfo.getName(), queueInfo.getRoutingType(), queueInfo.getFilterString(), false, queueInfo.isTemporary(), queueInfo.getMaxConsumers(), queueInfo.isPurgeOnNoConsumers(), queueInfo.isAutoCreated(), false); + CreateQueueMessage_V2 createQueueRequest = new CreateQueueMessage_V2(queueInfo.getAddress(), queueInfo.getName(), queueInfo.getRoutingType(), queueInfo.getFilterString(), false, queueInfo.isTemporary(), queueInfo.getMaxConsumers(), queueInfo.isPurgeOnNoConsumers(), queueInfo.isAutoCreated(), false, queueInfo.isExclusive(), queueInfo.isLastValue()); sendPacketWithoutLock(sessionChannel, createQueueRequest); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateQueueMessage_V2.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateQueueMessage_V2.java index 4c4b737514..4ffeb5ca84 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateQueueMessage_V2.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateQueueMessage_V2.java @@ -19,6 +19,7 @@ package org.apache.activemq.artemis.core.protocol.core.impl.wireformat; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.utils.BufferHelper; public class CreateQueueMessage_V2 extends CreateQueueMessage { @@ -30,6 +31,10 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { private boolean purgeOnNoConsumers; + private Boolean exclusive; + + private Boolean lastValue; + public CreateQueueMessage_V2(final SimpleString address, final SimpleString queueName, final RoutingType routingType, @@ -39,7 +44,9 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { final int maxConsumers, final boolean purgeOnNoConsumers, final boolean autoCreated, - final boolean requiresResponse) { + final boolean requiresResponse, + final Boolean exclusive, + final Boolean lastValue) { this(); this.address = address; @@ -52,6 +59,8 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { this.routingType = routingType; this.maxConsumers = maxConsumers; this.purgeOnNoConsumers = purgeOnNoConsumers; + this.exclusive = exclusive; + this.lastValue = lastValue; } public CreateQueueMessage_V2() { @@ -67,6 +76,8 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { buff.append(", routingType=" + routingType); buff.append(", maxConsumers=" + maxConsumers); buff.append(", purgeOnNoConsumers=" + purgeOnNoConsumers); + buff.append(", exclusive=" + exclusive); + buff.append(", lastValue=" + lastValue); buff.append("]"); return buff.toString(); } @@ -103,6 +114,22 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { this.autoCreated = autoCreated; } + public Boolean isExclusive() { + return exclusive; + } + + public void setExclusive(Boolean exclusive) { + this.exclusive = exclusive; + } + + public Boolean isLastValue() { + return lastValue; + } + + public void setLastValue(Boolean lastValue) { + this.lastValue = lastValue; + } + @Override public void encodeRest(final ActiveMQBuffer buffer) { super.encodeRest(buffer); @@ -110,6 +137,8 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { buffer.writeByte(routingType == null ? -1 : routingType.getType()); buffer.writeInt(maxConsumers); buffer.writeBoolean(purgeOnNoConsumers); + BufferHelper.writeNullableBoolean(buffer, exclusive); + BufferHelper.writeNullableBoolean(buffer, lastValue); } @Override @@ -119,6 +148,10 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { routingType = RoutingType.getType(buffer.readByte()); maxConsumers = buffer.readInt(); purgeOnNoConsumers = buffer.readBoolean(); + if (buffer.readableBytes() > 0) { + exclusive = BufferHelper.readNullableBoolean(buffer); + lastValue = BufferHelper.readNullableBoolean(buffer); + } } @Override @@ -129,6 +162,8 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { result = prime * result + (routingType.getType()); result = prime * result + (maxConsumers); result = prime * result + (purgeOnNoConsumers ? 1231 : 1237); + result = prime * result + (exclusive == null ? 0 : exclusive ? 1231 : 1237); + result = prime * result + (lastValue == null ? 0 : lastValue ? 1231 : 1237); return result; } @@ -147,7 +182,9 @@ public class CreateQueueMessage_V2 extends CreateQueueMessage { return false; if (purgeOnNoConsumers != other.purgeOnNoConsumers) return false; - if (purgeOnNoConsumers != other.purgeOnNoConsumers) + if (exclusive != other.exclusive) + return false; + if (lastValue != other.lastValue) return false; if (routingType == null) { if (other.routingType != null) diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateSharedQueueMessage_V2.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateSharedQueueMessage_V2.java index beebd12978..87630434fb 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateSharedQueueMessage_V2.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/CreateSharedQueueMessage_V2.java @@ -19,16 +19,25 @@ package org.apache.activemq.artemis.core.protocol.core.impl.wireformat; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.utils.BufferHelper; public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { private RoutingType routingType; + Integer maxConsumers; + Boolean purgeOnNoConsumers; + private Boolean exclusive; + private Boolean lastValue; public CreateSharedQueueMessage_V2(final SimpleString address, final SimpleString queueName, final RoutingType routingType, final SimpleString filterString, final boolean durable, + final Integer maxConsumers, + final Boolean purgeOnNoConsumers, + final Boolean exclusive, + final Boolean lastValue, final boolean requiresResponse) { this(); @@ -36,8 +45,13 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { this.queueName = queueName; this.filterString = filterString; this.durable = durable; - this.requiresResponse = requiresResponse; this.routingType = routingType; + this.maxConsumers = maxConsumers; + this.purgeOnNoConsumers = purgeOnNoConsumers; + this.exclusive = exclusive; + this.lastValue = lastValue; + this.requiresResponse = requiresResponse; + } public CreateSharedQueueMessage_V2() { @@ -52,6 +66,38 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { this.routingType = routingType; } + public Integer getMaxConsumers() { + return maxConsumers; + } + + public void setMaxConsumers(Integer maxConsumers) { + this.maxConsumers = maxConsumers; + } + + public Boolean isPurgeOnNoConsumers() { + return purgeOnNoConsumers; + } + + public void setPurgeOnNoConsumers(Boolean purgeOnNoConsumers) { + this.purgeOnNoConsumers = purgeOnNoConsumers; + } + + public Boolean isExclusive() { + return exclusive; + } + + public void setExclusive(Boolean exclusive) { + this.exclusive = exclusive; + } + + public Boolean isLastValue() { + return lastValue; + } + + public void setLastValue(Boolean lastValue) { + this.lastValue = lastValue; + } + @Override public String toString() { StringBuffer buff = new StringBuffer(getParentString()); @@ -59,6 +105,11 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { buff.append(", queueName=" + queueName); buff.append(", filterString=" + filterString); buff.append(", durable=" + durable); + buff.append(", routingType=" + routingType); + buff.append(", maxConsumers=" + maxConsumers); + buff.append(", purgeOnNoConsumers=" + purgeOnNoConsumers); + buff.append(", exclusive=" + exclusive); + buff.append(", lastValue=" + lastValue); buff.append(", requiresResponse=" + requiresResponse); buff.append("]"); return buff.toString(); @@ -72,6 +123,10 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { buffer.writeBoolean(durable); buffer.writeByte(routingType.getType()); buffer.writeBoolean(requiresResponse); + BufferHelper.writeNullableInteger(buffer, maxConsumers); + BufferHelper.writeNullableBoolean(buffer, purgeOnNoConsumers); + BufferHelper.writeNullableBoolean(buffer, exclusive); + BufferHelper.writeNullableBoolean(buffer, lastValue); } @Override @@ -82,6 +137,12 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { durable = buffer.readBoolean(); routingType = RoutingType.getType(buffer.readByte()); requiresResponse = buffer.readBoolean(); + if (buffer.readableBytes() > 0) { + maxConsumers = BufferHelper.readNullableInteger(buffer); + purgeOnNoConsumers = BufferHelper.readNullableBoolean(buffer); + exclusive = BufferHelper.readNullableBoolean(buffer); + lastValue = BufferHelper.readNullableBoolean(buffer); + } } @Override @@ -94,6 +155,10 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { result = prime * result + (durable ? 1231 : 1237); result = prime * result + routingType.getType(); result = prime * result + (requiresResponse ? 1231 : 1237); + result = prime * result + (maxConsumers == null ? 0 : maxConsumers.hashCode()); + result = prime * result + (purgeOnNoConsumers == null ? 0 : purgeOnNoConsumers ? 1231 : 1237); + result = prime * result + (exclusive == null ? 0 : exclusive ? 1231 : 1237); + result = prime * result + (lastValue == null ? 0 : lastValue ? 1231 : 1237); return result; } @@ -127,6 +192,14 @@ public class CreateSharedQueueMessage_V2 extends CreateSharedQueueMessage { return false; if (requiresResponse != other.requiresResponse) return false; + if (maxConsumers != other.maxConsumers) + return false; + if (purgeOnNoConsumers != other.purgeOnNoConsumers) + return false; + if (exclusive != other.exclusive) + return false; + if (lastValue != other.lastValue) + return false; return true; } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionBindingQueryResponseMessage_V4.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionBindingQueryResponseMessage_V4.java index dc0e9581a6..0d08d99078 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionBindingQueryResponseMessage_V4.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionBindingQueryResponseMessage_V4.java @@ -20,6 +20,7 @@ import java.util.List; import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.utils.BufferHelper; public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryResponseMessage_V3 { @@ -27,12 +28,18 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe private int defaultMaxConsumers; + private Boolean defaultExclusive; + + private Boolean defaultLastValue; + public SessionBindingQueryResponseMessage_V4(final boolean exists, final List queueNames, final boolean autoCreateQueues, final boolean autoCreateAddresses, final boolean defaultPurgeOnNoConsumers, - final int defaultMaxConsumers) { + final int defaultMaxConsumers, + final Boolean defaultExclusive, + final Boolean defaultLastValue) { super(SESS_BINDINGQUERY_RESP_V4); this.exists = exists; @@ -46,6 +53,10 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe this.defaultPurgeOnNoConsumers = defaultPurgeOnNoConsumers; this.defaultMaxConsumers = defaultMaxConsumers; + + this.defaultExclusive = defaultExclusive; + + this.defaultLastValue = defaultLastValue; } public SessionBindingQueryResponseMessage_V4() { @@ -60,11 +71,21 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe return defaultMaxConsumers; } + public Boolean isDefaultExclusive() { + return defaultExclusive; + } + + public Boolean isDefaultLastValue() { + return defaultLastValue; + } + @Override public void encodeRest(final ActiveMQBuffer buffer) { super.encodeRest(buffer); buffer.writeBoolean(defaultPurgeOnNoConsumers); buffer.writeInt(defaultMaxConsumers); + BufferHelper.writeNullableBoolean(buffer, defaultExclusive); + BufferHelper.writeNullableBoolean(buffer, defaultLastValue); } @Override @@ -72,6 +93,10 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe super.decodeRest(buffer); defaultPurgeOnNoConsumers = buffer.readBoolean(); defaultMaxConsumers = buffer.readInt(); + if (buffer.readableBytes() > 0) { + defaultExclusive = BufferHelper.readNullableBoolean(buffer); + defaultLastValue = BufferHelper.readNullableBoolean(buffer); + } } @Override @@ -80,6 +105,8 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe int result = super.hashCode(); result = prime * result + (defaultPurgeOnNoConsumers ? 1231 : 1237); result = prime * result + defaultMaxConsumers; + result = prime * result + (defaultExclusive == null ? 0 : defaultExclusive ? 1231 : 1237); + result = prime * result + (defaultLastValue == null ? 0 : defaultLastValue ? 1231 : 1237); return result; } @@ -95,6 +122,8 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe StringBuffer buff = new StringBuffer(super.getParentString()); buff.append(", defaultPurgeOnNoConsumers=" + defaultPurgeOnNoConsumers); buff.append(", defaultMaxConsumers=" + defaultMaxConsumers); + buff.append(", defaultExclusive=" + defaultExclusive); + buff.append(", defaultLastValue=" + defaultLastValue); return buff.toString(); } @@ -111,6 +140,10 @@ public class SessionBindingQueryResponseMessage_V4 extends SessionBindingQueryRe return false; if (defaultMaxConsumers != other.defaultMaxConsumers) return false; + if (defaultExclusive != other.defaultExclusive) + return false; + if (defaultLastValue != other.defaultLastValue) + return false; return true; } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java index bde65935e9..12f2a7b962 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/SessionQueueQueryResponseMessage_V3.java @@ -22,6 +22,7 @@ import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.core.client.impl.QueueQueryImpl; import org.apache.activemq.artemis.core.server.QueueQueryResult; import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.utils.BufferHelper; public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryResponseMessage_V2 { @@ -33,12 +34,16 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon protected int maxConsumers; + protected Boolean exclusive; + + protected Boolean lastValue; + public SessionQueueQueryResponseMessage_V3(final QueueQueryResult result) { - this(result.getName(), result.getAddress(), result.isDurable(), result.isTemporary(), result.getFilterString(), result.getConsumerCount(), result.getMessageCount(), result.isExists(), result.isAutoCreateQueues(), result.isAutoCreated(), result.isPurgeOnNoConsumers(), result.getRoutingType(), result.getMaxConsumers()); + this(result.getName(), result.getAddress(), result.isDurable(), result.isTemporary(), result.getFilterString(), result.getConsumerCount(), result.getMessageCount(), result.isExists(), result.isAutoCreateQueues(), result.isAutoCreated(), result.isPurgeOnNoConsumers(), result.getRoutingType(), result.getMaxConsumers(), result.isExclusive(), result.isLastValue()); } public SessionQueueQueryResponseMessage_V3() { - this(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, -1); + this(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, -1, null, null); } private SessionQueueQueryResponseMessage_V3(final SimpleString name, @@ -53,7 +58,9 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon final boolean autoCreated, final boolean purgeOnNoConsumers, final RoutingType routingType, - final int maxConsumers) { + final int maxConsumers, + final Boolean exclusive, + final Boolean lastValue) { super(SESS_QUEUEQUERY_RESP_V3); this.durable = durable; @@ -81,6 +88,10 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon this.routingType = routingType; this.maxConsumers = maxConsumers; + + this.exclusive = exclusive; + + this.lastValue = lastValue; } public boolean isAutoCreated() { @@ -115,6 +126,22 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon this.maxConsumers = maxConsumers; } + public Boolean isExclusive() { + return exclusive; + } + + public void setExclusive(Boolean exclusive) { + this.exclusive = exclusive; + } + + public Boolean isLastValue() { + return lastValue; + } + + public void setLastValue(Boolean lastValue) { + this.lastValue = lastValue; + } + @Override public void encodeRest(final ActiveMQBuffer buffer) { super.encodeRest(buffer); @@ -122,6 +149,8 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon buffer.writeBoolean(purgeOnNoConsumers); buffer.writeByte(routingType.getType()); buffer.writeInt(maxConsumers); + BufferHelper.writeNullableBoolean(buffer, exclusive); + BufferHelper.writeNullableBoolean(buffer, lastValue); } @Override @@ -131,6 +160,10 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon purgeOnNoConsumers = buffer.readBoolean(); routingType = RoutingType.getType(buffer.readByte()); maxConsumers = buffer.readInt(); + if (buffer.readableBytes() > 0) { + exclusive = BufferHelper.readNullableBoolean(buffer); + lastValue = BufferHelper.readNullableBoolean(buffer); + } } @Override @@ -141,6 +174,8 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon result = prime * result + (purgeOnNoConsumers ? 1231 : 1237); result = prime * result + routingType.hashCode(); result = prime * result + maxConsumers; + result = prime * result + (exclusive == null ? 0 : exclusive ? 1231 : 1237); + result = prime * result + (lastValue == null ? 0 : lastValue ? 1231 : 1237); return result; } @@ -158,12 +193,14 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon buff.append(", purgeOnNoConsumers=" + purgeOnNoConsumers); buff.append(", routingType=" + routingType); buff.append(", maxConsumers=" + maxConsumers); + buff.append(", exclusive=" + exclusive); + buff.append(", lastValue=" + lastValue); return buff.toString(); } @Override public ClientSession.QueueQuery toQueueQuery() { - return new QueueQueryImpl(isDurable(), isTemporary(), getConsumerCount(), getMessageCount(), getFilterString(), getAddress(), getName(), isExists(), isAutoCreateQueues(), getMaxConsumers(), isAutoCreated(), isPurgeOnNoConsumers(), getRoutingType()); + return new QueueQueryImpl(isDurable(), isTemporary(), getConsumerCount(), getMessageCount(), getFilterString(), getAddress(), getName(), isExists(), isAutoCreateQueues(), getMaxConsumers(), isAutoCreated(), isPurgeOnNoConsumers(), getRoutingType(), isExclusive(), isLastValue()); } @Override @@ -179,6 +216,10 @@ public class SessionQueueQueryResponseMessage_V3 extends SessionQueueQueryRespon return false; if (purgeOnNoConsumers != other.purgeOnNoConsumers) return false; + if (exclusive != other.exclusive) + return false; + if (lastValue != other.lastValue) + return false; if (routingType == null) { if (other.routingType != null) return false; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java index cf88d6224c..6497e3f99a 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/server/QueueQueryResult.java @@ -47,6 +47,10 @@ public class QueueQueryResult { private int maxConsumers; + private Boolean exclusive; + + private Boolean lastValue; + public QueueQueryResult(final SimpleString name, final SimpleString address, final boolean durable, @@ -59,7 +63,9 @@ public class QueueQueryResult { final boolean autoCreated, final boolean purgeOnNoConsumers, final RoutingType routingType, - final int maxConsumers) { + final int maxConsumers, + final Boolean exclusive, + final Boolean lastValue) { this.durable = durable; this.temporary = temporary; @@ -85,6 +91,10 @@ public class QueueQueryResult { this.routingType = routingType; this.maxConsumers = maxConsumers; + + this.exclusive = exclusive; + + this.lastValue = lastValue; } public boolean isExists() { @@ -142,4 +152,12 @@ public class QueueQueryResult { public void setAddress(SimpleString address) { this.address = address; } + + public Boolean isExclusive() { + return exclusive; + } + + public Boolean isLastValue() { + return lastValue; + } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java index 1c85f08c50..058e6063d5 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/spi/core/remoting/SessionContext.java @@ -171,8 +171,20 @@ public abstract class SessionContext { * @param routingType * @param filterString * @param durable + * @param exclusive + * @param lastValue * @throws ActiveMQException */ + public abstract void createSharedQueue(SimpleString address, + SimpleString queueName, + RoutingType routingType, + SimpleString filterString, + boolean durable, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Boolean lastValue) throws ActiveMQException; + public abstract void createSharedQueue(SimpleString address, SimpleString queueName, RoutingType routingType, @@ -199,6 +211,7 @@ public abstract class SessionContext { boolean temp, boolean autoCreated) throws ActiveMQException; + @Deprecated public abstract void createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, @@ -209,6 +222,18 @@ public abstract class SessionContext { boolean purgeOnNoConsumers, boolean autoCreated) throws ActiveMQException; + public abstract void createQueue(SimpleString address, + RoutingType routingType, + SimpleString queueName, + SimpleString filterString, + boolean durable, + boolean temp, + int maxConsumers, + boolean purgeOnNoConsumers, + boolean autoCreated, + Boolean exclusive, + Boolean lastVale) throws ActiveMQException; + public abstract ClientSession.QueueQuery queueQuery(SimpleString queueName) throws ActiveMQException; public abstract void forceDelivery(ClientConsumer consumer, long sequence) throws ActiveMQException; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java index a5ab245fc9..c5e4884ce6 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQDestination.java @@ -24,8 +24,10 @@ import java.util.Properties; import java.util.UUID; import org.apache.activemq.artemis.api.core.Pair; +import org.apache.activemq.artemis.api.core.QueueAttributes; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.jndi.JNDIStorable; +import org.apache.activemq.artemis.api.core.ParameterisedAddress; /** * ActiveMQ Artemis implementation of a JMS Destination. @@ -249,6 +251,11 @@ public class ActiveMQDestination extends JNDIStorable implements Destination, Se */ private SimpleString simpleAddress; + /** + * Queue parameters; + */ + private QueueAttributes queueAttributes; + /** * Needed for serialization backwards compatibility. */ @@ -280,7 +287,10 @@ public class ActiveMQDestination extends JNDIStorable implements Destination, Se protected ActiveMQDestination(final SimpleString address, final TYPE type, final ActiveMQSession session) { - this.simpleAddress = address; + + if (address != null) { + setSimpleAddress(address); + } this.thetype = type; @@ -319,8 +329,16 @@ public class ActiveMQDestination extends JNDIStorable implements Destination, Se if (address == null) { throw new IllegalArgumentException("address cannot be null"); } - this.address = address.toString(); - this.simpleAddress = address; + if (ParameterisedAddress.isParameterised(address)) { + ParameterisedAddress parameteredAddress = new ParameterisedAddress(address); + this.simpleAddress = parameteredAddress.getAddress(); + this.address = parameteredAddress.getAddress().toString(); + this.queueAttributes = parameteredAddress.getQueueAttributes(); + } else { + this.simpleAddress = address; + this.address = address.toString(); + this.queueAttributes = null; + } } public void delete() throws JMSException { @@ -351,6 +369,10 @@ public class ActiveMQDestination extends JNDIStorable implements Destination, Se return simpleAddress; } + public QueueAttributes getQueueAttributes() { + return queueAttributes; + } + public String getName() { return name != null ? name : getAddress(); } diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageProducer.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageProducer.java index 9f86e493e2..ae1d270e0d 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageProducer.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageProducer.java @@ -37,6 +37,7 @@ import javax.jms.TopicPublisher; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException; import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException; +import org.apache.activemq.artemis.api.core.QueueAttributes; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; @@ -412,7 +413,7 @@ public class ActiveMQMessageProducer implements MessageProducer, QueueSender, To // TODO is it right to use the address for the queue name here? clientSession.createTemporaryQueue(address, RoutingType.ANYCAST, address); } else { - clientSession.createQueue(address, RoutingType.ANYCAST, address, null, true, true, query.getDefaultMaxConsumers(), query.isDefaultPurgeOnNoConsumers()); + createQueue(destination, RoutingType.ANYCAST, address, null, true, true, query.getDefaultMaxConsumers(), query.isDefaultPurgeOnNoConsumers(), query.isDefaultExclusive(), query.isDefaultLastValueQueue()); } } else if (!destination.isQueue() && query.isAutoCreateAddresses()) { clientSession.createAddress(address, RoutingType.MULTICAST, true); @@ -427,7 +428,7 @@ public class ActiveMQMessageProducer implements MessageProducer, QueueSender, To if (destination.isTemporary()) { clientSession.createTemporaryQueue(address, RoutingType.ANYCAST, address); } else { - clientSession.createQueue(address, RoutingType.ANYCAST, address, null, true, true, query.getDefaultMaxConsumers(), query.isDefaultPurgeOnNoConsumers()); + createQueue(destination, RoutingType.ANYCAST, address, null, true, true, query.getDefaultMaxConsumers(), query.isDefaultPurgeOnNoConsumers(), query.isDefaultExclusive(), query.isDefaultLastValueQueue()); } } } @@ -535,6 +536,27 @@ public class ActiveMQMessageProducer implements MessageProducer, QueueSender, To } } + private void createQueue(ActiveMQDestination destination, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + QueueAttributes queueAttributes = destination.getQueueAttributes(); + if (queueAttributes == null) { + clientSession.createQueue(destination.getSimpleAddress(), routingType, queueName, filter, durable, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue); + } else { + clientSession.createQueue( + destination.getSimpleAddress(), + routingType, + queueName, + filter, + durable, + autoCreated, + queueAttributes.getMaxConsumers() == null ? maxConsumers : queueAttributes.getMaxConsumers(), + queueAttributes.getPurgeOnNoConsumers() == null ? purgeOnNoConsumers : queueAttributes.getPurgeOnNoConsumers(), + queueAttributes.getExclusive() == null ? exclusive : queueAttributes.getExclusive(), + queueAttributes.getLastValue() == null ? lastValue : queueAttributes.getLastValue() + ); + } + } + + private static final class CompletionListenerWrapper implements SendAcknowledgementHandler { private final CompletionListener completionListener; diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java index cf2ec59d3d..a0ccfcca8d 100644 --- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java +++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQSession.java @@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQQueueExistsException; +import org.apache.activemq.artemis.api.core.QueueAttributes; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientProducer; @@ -318,7 +319,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { if (jbd.isQueue() && response.isAutoCreateQueues()) { // perhaps just relying on the broker to do it is simplest (i.e. purgeOnNoConsumers) session.createAddress(jbd.getSimpleAddress(), RoutingType.ANYCAST, true); - session.createQueue(jbd.getSimpleAddress(), RoutingType.ANYCAST, jbd.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers()); + createQueue(jbd, RoutingType.ANYCAST, jbd.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } else if (!jbd.isQueue() && response.isAutoCreateAddresses()) { session.createAddress(jbd.getSimpleAddress(), RoutingType.MULTICAST, true); } else { @@ -629,16 +630,16 @@ public class ActiveMQSession implements QueueSession, TopicSession { queueName = ActiveMQDestination.createQueueNameForSubscription(durability == ConsumerDurability.DURABLE, connection.getClientID(), subscriptionName); - if (durability == ConsumerDurability.DURABLE) { - try { - session.createSharedQueue(dest.getSimpleAddress(), RoutingType.MULTICAST, queueName, coreFilterString, true); - } catch (ActiveMQQueueExistsException ignored) { - // We ignore this because querying and then creating the queue wouldn't be idempotent - // we could also add a parameter to ignore existence what would require a bigger work around to avoid - // compatibility. + try { + if (durability == ConsumerDurability.DURABLE) { + createSharedQueue(dest, RoutingType.MULTICAST, queueName, coreFilterString, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); + } else { + createSharedQueue(dest, RoutingType.MULTICAST, queueName, coreFilterString, false, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } - } else { - session.createSharedQueue(dest.getSimpleAddress(), queueName, coreFilterString, false); + } catch (ActiveMQQueueExistsException ignored) { + // We ignore this because querying and then creating the queue wouldn't be idempotent + // we could also add a parameter to ignore existence what would require a bigger work around to avoid + // compatibility. } consumer = session.createConsumer(queueName, null, false); @@ -699,7 +700,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { if (!response.isExists() || !response.getQueueNames().contains(dest.getSimpleAddress())) { if (response.isAutoCreateQueues()) { try { - session.createQueue(dest.getSimpleAddress(), RoutingType.ANYCAST, dest.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers()); + createQueue(dest, RoutingType.ANYCAST, dest.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } catch (ActiveMQQueueExistsException e) { // The queue was created by another client/admin between the query check and send create queue packet } @@ -733,7 +734,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { queueName = new SimpleString(UUID.randomUUID().toString()); - session.createTemporaryQueue(dest.getSimpleAddress(), RoutingType.MULTICAST, queueName, coreFilterString); + createTemporaryQueue(dest, RoutingType.MULTICAST, queueName, coreFilterString, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); consumer = session.createConsumer(queueName, null, false); @@ -756,7 +757,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { if (!subResponse.isExists()) { // durable subscription queues are not technically considered to be auto-created - session.createQueue(dest.getSimpleAddress(), RoutingType.MULTICAST, queueName, coreFilterString, true, false, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers()); + createQueue(dest, RoutingType.MULTICAST, queueName, coreFilterString, true, false, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } else { // Already exists if (subResponse.getConsumerCount() > 0) { @@ -787,7 +788,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { session.deleteQueue(queueName); // Create the new one - session.createQueue(dest.getSimpleAddress(), RoutingType.MULTICAST, queueName, coreFilterString, true, false, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers()); + createQueue(dest, RoutingType.MULTICAST, queueName, coreFilterString, true, false, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } } @@ -849,7 +850,7 @@ public class ActiveMQSession implements QueueSession, TopicSession { AddressQuery response = session.addressQuery(new SimpleString(activeMQDestination.getAddress())); if (!response.isExists()) { if (response.isAutoCreateQueues()) { - session.createQueue(activeMQDestination.getSimpleAddress(), RoutingType.ANYCAST, activeMQDestination.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers()); + createQueue(activeMQDestination, RoutingType.ANYCAST, activeMQDestination.getSimpleAddress(), null, true, true, response.getDefaultMaxConsumers(), response.isDefaultPurgeOnNoConsumers(), response.isDefaultExclusive(), response.isDefaultLastValueQueue()); } else { throw new InvalidDestinationException("Destination " + activeMQDestination.getName() + " does not exist"); } @@ -1154,6 +1155,63 @@ public class ActiveMQSession implements QueueSession, TopicSession { } } + private void createTemporaryQueue(ActiveMQDestination destination, RoutingType routingType, SimpleString queueName, SimpleString filter, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + QueueAttributes queueAttributes = destination.getQueueAttributes(); + if (queueAttributes == null) { + session.createTemporaryQueue(destination.getSimpleAddress(), routingType, queueName, filter, maxConsumers, purgeOnNoConsumers, exclusive, lastValue); + } else { + session.createTemporaryQueue( + destination.getSimpleAddress(), + routingType, + queueName, + filter, + queueAttributes.getMaxConsumers() == null ? maxConsumers : queueAttributes.getMaxConsumers(), + queueAttributes.getPurgeOnNoConsumers() == null ? purgeOnNoConsumers : queueAttributes.getPurgeOnNoConsumers(), + queueAttributes.getExclusive() == null ? exclusive : queueAttributes.getExclusive(), + queueAttributes.getLastValue() == null ? lastValue : queueAttributes.getLastValue() + ); + } + } + + private void createSharedQueue(ActiveMQDestination destination, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + QueueAttributes queueAttributes = destination.getQueueAttributes(); + if (queueAttributes == null) { + session.createSharedQueue(destination.getSimpleAddress(), routingType, queueName, filter, durable, maxConsumers, purgeOnNoConsumers, exclusive, lastValue); + } else { + session.createSharedQueue( + destination.getSimpleAddress(), + routingType, + queueName, + filter, + durable, + queueAttributes.getMaxConsumers() == null ? maxConsumers : queueAttributes.getMaxConsumers(), + queueAttributes.getPurgeOnNoConsumers() == null ? purgeOnNoConsumers : queueAttributes.getPurgeOnNoConsumers(), + queueAttributes.getExclusive() == null ? exclusive : queueAttributes.getExclusive(), + queueAttributes.getLastValue() == null ? lastValue : queueAttributes.getLastValue() + ); + } + } + + private void createQueue(ActiveMQDestination destination, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + QueueAttributes queueAttributes = destination.getQueueAttributes(); + if (queueAttributes == null) { + session.createQueue(destination.getSimpleAddress(), routingType, queueName, filter, durable, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue); + } else { + session.createQueue( + destination.getSimpleAddress(), + routingType, + queueName, + filter, + durable, + autoCreated, + queueAttributes.getMaxConsumers() == null ? maxConsumers : queueAttributes.getMaxConsumers(), + queueAttributes.getPurgeOnNoConsumers() == null ? purgeOnNoConsumers : queueAttributes.getPurgeOnNoConsumers(), + queueAttributes.getExclusive() == null ? exclusive : queueAttributes.getExclusive(), + queueAttributes.getLastValue() == null ? lastValue : queueAttributes.getLastValue() + ); + } + } + // Inner classes ------------------------------------------------- } diff --git a/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQParameterTest.java b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQParameterTest.java new file mode 100644 index 0000000000..1cb179da67 --- /dev/null +++ b/artemis-jms-client/src/test/java/org/apache/activemq/artemis/jms/client/ActiveMQParameterTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.jms.client; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Test Address queue parameters are correctly read. + */ +public class ActiveMQParameterTest { + + @Test + public void testQueueParameters() { + ActiveMQDestination activeMQDestination = new ActiveMQQueue("jms.queue.foo?exclusive=true"); + assertTrue(activeMQDestination.getQueueAttributes().getExclusive()); + + assertEquals("jms.queue.foo", activeMQDestination.getAddress()); + + activeMQDestination = new ActiveMQQueue("jms.queue.foo?exclusive=false"); + assertFalse(activeMQDestination.getQueueAttributes().getExclusive()); + + activeMQDestination = new ActiveMQQueue("jms.queue.foo?last-value=true"); + assertTrue(activeMQDestination.getQueueAttributes().getLastValue()); + + activeMQDestination = new ActiveMQQueue("jms.queue.foo?last-value=false"); + assertFalse(activeMQDestination.getQueueAttributes().getLastValue()); + + } + + @Test + public void testMultipleQueueParameters() { + ActiveMQDestination activeMQDestination = new ActiveMQQueue("jms.queue.foo?last-value=true&exclusive=true"); + assertEquals("jms.queue.foo", activeMQDestination.getAddress()); + assertTrue(activeMQDestination.getQueueAttributes().getLastValue()); + assertTrue(activeMQDestination.getQueueAttributes().getExclusive()); + } + + @Test + public void testNoQueueParameters() { + ActiveMQDestination activeMQDestination = new ActiveMQQueue("jms.queue.foo"); + assertEquals("jms.queue.foo", activeMQDestination.getAddress()); + assertNull(activeMQDestination.getQueueAttributes()); + } +} diff --git a/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java b/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java index 185feaf0a5..ee3520fa1e 100644 --- a/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java +++ b/artemis-protocols/artemis-hqclient-protocol/src/main/java/org/apache/activemq/artemis/core/protocol/hornetq/client/HornetQClientSessionContext.java @@ -72,7 +72,7 @@ public class HornetQClientSessionContext extends ActiveMQSessionContext { public ClientSession.AddressQuery addressQuery(final SimpleString address) throws ActiveMQException { SessionBindingQueryResponseMessage response = (SessionBindingQueryResponseMessage) getSessionChannel().sendBlocking(new SessionBindingQueryMessage(address), PacketImpl.SESS_BINDINGQUERY_RESP); - return new AddressQueryImpl(response.isExists(), response.getQueueNames(), false, false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()); + return new AddressQueryImpl(response.isExists(), response.getQueueNames(), false, false, ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultLastValue()); } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java index 6648b7ed88..236fac6526 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/CoreQueueConfiguration.java @@ -35,6 +35,10 @@ public class CoreQueueConfiguration implements Serializable { private String user = null; + private Boolean exclusive; + + private Boolean lastValue; + private Integer maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(); private Boolean purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); @@ -64,6 +68,14 @@ public class CoreQueueConfiguration implements Serializable { return user; } + public Boolean isExclusive() { + return exclusive; + } + + public Boolean isLastValue() { + return lastValue; + } + /** * @param address the address to set */ @@ -120,6 +132,16 @@ public class CoreQueueConfiguration implements Serializable { return this; } + public CoreQueueConfiguration setExclusive(Boolean exclusive) { + this.exclusive = exclusive; + return this; + } + + public CoreQueueConfiguration setLastValue(Boolean lastValue) { + this.lastValue = lastValue; + return this; + } + public boolean getPurgeOnNoConsumers() { return purgeOnNoConsumers; } @@ -147,6 +169,8 @@ public class CoreQueueConfiguration implements Serializable { result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((maxConsumers == null) ? 0 : maxConsumers.hashCode()); result = prime * result + ((purgeOnNoConsumers == null) ? 0 : purgeOnNoConsumers.hashCode()); + result = prime * result + ((exclusive == null) ? 0 : exclusive.hashCode()); + result = prime * result + ((lastValue == null) ? 0 : lastValue.hashCode()); return result; } @@ -187,6 +211,18 @@ public class CoreQueueConfiguration implements Serializable { } else if (!purgeOnNoConsumers.equals(other.purgeOnNoConsumers)) { return false; } + if (exclusive == null) { + if (other.exclusive != null) + return false; + } else if (!exclusive.equals(other.exclusive)) { + return false; + } + if (lastValue == null) { + if (other.lastValue != null) + return false; + } else if (!lastValue.equals(other.lastValue)) { + return false; + } return true; } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 8eee4dc86d..b5352ba2cd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -179,6 +179,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { private static final String LVQ_NODE_NAME = "last-value-queue"; + private static final String DEFAULT_LVQ_NODE_NAME = "default-last-value-queue"; + + private static final String DEFAULT_EXCLUSIVE_NODE_NAME = "default-exclusive-queue"; + private static final String REDISTRIBUTION_DELAY_NODE_NAME = "redistribution-delay"; private static final String SEND_TO_DLA_ON_NO_ROUTE = "send-to-dla-on-no-route"; @@ -989,8 +993,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { Validators.ADDRESS_FULL_MESSAGE_POLICY_TYPE.validate(ADDRESS_FULL_MESSAGE_POLICY_NODE_NAME, value); AddressFullMessagePolicy policy = Enum.valueOf(AddressFullMessagePolicy.class, value); addressSettings.setAddressFullMessagePolicy(policy); - } else if (LVQ_NODE_NAME.equalsIgnoreCase(name)) { - addressSettings.setLastValueQueue(XMLUtil.parseBoolean(child)); + } else if (LVQ_NODE_NAME.equalsIgnoreCase(name) || DEFAULT_LVQ_NODE_NAME.equalsIgnoreCase(name)) { + addressSettings.setDefaultLastValueQueue(XMLUtil.parseBoolean(child)); + } else if (DEFAULT_EXCLUSIVE_NODE_NAME.equalsIgnoreCase(name)) { + addressSettings.setDefaultExclusiveQueue(XMLUtil.parseBoolean(child)); } else if (MAX_DELIVERY_ATTEMPTS.equalsIgnoreCase(name)) { addressSettings.setMaxDeliveryAttempts(XMLUtil.parseInt(child)); } else if (REDISTRIBUTION_DELAY_NODE_NAME.equalsIgnoreCase(name)) { @@ -1090,6 +1096,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { int maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(); boolean purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); String user = null; + Boolean exclusive = null; + Boolean lastValue = null; NamedNodeMap attributes = node.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { @@ -1099,6 +1107,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { Validators.MAX_QUEUE_CONSUMERS.validate(name, maxConsumers); } else if (item.getNodeName().equals("purge-on-no-consumers")) { purgeOnNoConsumers = Boolean.parseBoolean(item.getNodeValue()); + } else if (item.getNodeName().equals("exclusive")) { + exclusive = Boolean.parseBoolean(item.getNodeValue()); + } else if (item.getNodeName().equals("last-value")) { + lastValue = Boolean.parseBoolean(item.getNodeValue()); } } @@ -1117,7 +1129,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { } } - return new CoreQueueConfiguration().setAddress(address).setName(name).setFilterString(filterString).setDurable(durable).setMaxConsumers(maxConsumers).setPurgeOnNoConsumers(purgeOnNoConsumers).setUser(user); + return new CoreQueueConfiguration().setAddress(address).setName(name).setFilterString(filterString).setDurable(durable).setMaxConsumers(maxConsumers).setPurgeOnNoConsumers(purgeOnNoConsumers).setUser(user) + .setExclusive(exclusive).setLastValue(lastValue); } protected CoreAddressConfiguration parseAddressConfiguration(final Node node) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index 4e80bb5527..41d7b30dc7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -823,12 +823,21 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active String routingType, Integer maxConsumers, Boolean purgeOnNoConsumers) throws Exception { + return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, null); + } + + @Override + public String updateQueue(String name, + String routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception { checkStarted(); clearIO(); try { - final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers); + final Queue queue = server.updateQueue(name, routingType != null ? RoutingType.valueOf(routingType) : null, maxConsumers, purgeOnNoConsumers, exclusive); if (queue == null) { throw ActiveMQMessageBundle.BUNDLE.noSuchQueue(new SimpleString(name)); } @@ -2077,7 +2086,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active .add("redeliveryMultiplier", addressSettings.getRedeliveryMultiplier()) .add("maxRedeliveryDelay", addressSettings.getMaxRedeliveryDelay()) .add("redistributionDelay", addressSettings.getRedistributionDelay()) - .add("lastValueQueue", addressSettings.isLastValueQueue()) + .add("lastValueQueue", addressSettings.isDefaultLastValueQueue()) .add("sendToDLAOnNoRoute", addressSettings.isSendToDLAOnNoRoute()) .add("addressFullMessagePolicy", policy) .add("slowConsumerThreshold", addressSettings.getSlowConsumerThreshold()) @@ -2163,7 +2172,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active addressSettings.setDeadLetterAddress(DLA == null ? null : new SimpleString(DLA)); addressSettings.setExpiryAddress(expiryAddress == null ? null : new SimpleString(expiryAddress)); addressSettings.setExpiryDelay(expiryDelay); - addressSettings.setLastValueQueue(lastValueQueue); + addressSettings.setDefaultLastValueQueue(lastValueQueue); addressSettings.setMaxDeliveryAttempts(deliveryAttempts); addressSettings.setPageCacheMaxSize(pageMaxCacheSize); addressSettings.setMaxSizeBytes(maxSizeBytes); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java index 0a1c3850c8..cefcbf9b28 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/QueueControlImpl.java @@ -381,6 +381,30 @@ public class QueueControlImpl extends AbstractControl implements QueueControl { } } + @Override + public boolean isExclusive() { + checkStarted(); + + clearIO(); + try { + return queue.isExclusive(); + } finally { + blockOnIO(); + } + } + + @Override + public boolean isLastValue() { + checkStarted(); + + clearIO(); + try { + return queue.isLastValue(); + } finally { + blockOnIO(); + } + } + @Override public Map[] listScheduledMessages() throws Exception { checkStarted(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/QueueView.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/QueueView.java index eb0f1a4b4b..181b6ce835 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/QueueView.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/QueueView.java @@ -60,7 +60,9 @@ public class QueueView extends ActiveMQAbstractView { .add("messagesAcked", toString(queue.getMessagesAcknowledged())) .add("deliveringCount", toString(queue.getDeliveringCount())) .add("messagesKilled", toString(queue.getMessagesKilled())) - .add("deliverDeliver", toString(q.isDirectDeliver())); + .add("deliverDeliver", toString(q.isDirectDeliver())) + .add("exclusive", toString(queue.isExclusive())) + .add("lastValue", toString(queue.isLastValue())); return obj; } @@ -108,6 +110,10 @@ public class QueueView extends ActiveMQAbstractView { return queue.getMessagesKilled(); case "deliverDeliver": return q.isDirectDeliver(); + case "exclusive": + return q.isExclusive(); + case "lastValue": + return q.isLastValue(); default: throw new IllegalArgumentException("Unsupported field, " + fieldName); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/predicate/QueueFilterPredicate.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/predicate/QueueFilterPredicate.java index d8209a785d..10a0e91055 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/predicate/QueueFilterPredicate.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/view/predicate/QueueFilterPredicate.java @@ -27,7 +27,7 @@ public class QueueFilterPredicate extends ActiveMQFilterPredicate enum Field { ID, NAME, CONSUMER_ID, QUEUE, ADDRESS, MAX_CONSUMERS, FILTER, MESSAGE_COUNT, CONSUMER_COUNT, DELIVERING_COUNT, MESSAGES_ADDED, MESSAGES_ACKED, RATE, ROUTING_TYPE, USER, AUTO_CREATED, DURABLE, PAUSED, TEMPORARY, - PURGE_ON_NO_CONSUMERS, MESSAGES_KILLED, DIRECT_DELIVER + PURGE_ON_NO_CONSUMERS, MESSAGES_KILLED, DIRECT_DELIVER, LAST_VALUE, EXCLUSIVE } private Field f; @@ -89,6 +89,10 @@ public class QueueFilterPredicate extends ActiveMQFilterPredicate return matches(queue.isPurgeOnNoConsumers()); case MESSAGES_KILLED: return matches(queue.getMessagesKilled()); + case EXCLUSIVE: + return matches(queue.isExclusive()); + case LAST_VALUE: + return matches(queue.isLastValue()); default: return true; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java index 7164cc110f..00b7974b41 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/QueueBindingInfo.java @@ -54,6 +54,10 @@ public interface QueueBindingInfo { void setPurgeOnNoConsumers(boolean purgeOnNoConsumers); + boolean isExclusive(); + + void setExclusive(boolean exclusive); + byte getRoutingType(); void setRoutingType(byte routingType); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java index 54413689f4..34d249e2a0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java @@ -1272,7 +1272,7 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp SimpleString filterString = filter == null ? null : filter.getFilterString(); - PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.getRoutingType().getType()); + PersistentQueueBindingEncoding bindingEncoding = new PersistentQueueBindingEncoding(queue.getName(), binding.getAddress(), filterString, queue.getUser(), queue.isAutoCreated(), queue.getMaxConsumers(), queue.isPurgeOnNoConsumers(), queue.isExclusive(), queue.getRoutingType().getType()); readLock(); try { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java index af2c675c1a..d67b300b46 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/codec/PersistentQueueBindingEncoding.java @@ -46,6 +46,8 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin public boolean purgeOnNoConsumers; + public boolean exclusive; + public byte routingType; public PersistentQueueBindingEncoding() { @@ -68,6 +70,8 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin maxConsumers + ", purgeOnNoConsumers=" + purgeOnNoConsumers + + ", exclusive=" + + exclusive + ", routingType=" + routingType + "]"; @@ -80,6 +84,7 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers, + final boolean exclusive, final byte routingType) { this.name = name; this.address = address; @@ -88,6 +93,7 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin this.autoCreated = autoCreated; this.maxConsumers = maxConsumers; this.purgeOnNoConsumers = purgeOnNoConsumers; + this.exclusive = exclusive; this.routingType = routingType; } @@ -163,6 +169,16 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin this.purgeOnNoConsumers = purgeOnNoConsumers; } + @Override + public boolean isExclusive() { + return exclusive; + } + + @Override + public void setExclusive(boolean exclusive) { + this.exclusive = exclusive; + } + @Override public byte getRoutingType() { return routingType; @@ -203,6 +219,12 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); routingType = ActiveMQDefaultConfiguration.getDefaultRoutingType().getType(); } + + if (buffer.readableBytes() > 0) { + exclusive = buffer.readBoolean(); + } else { + exclusive = ActiveMQDefaultConfiguration.getDefaultExclusive(); + } } @Override @@ -215,6 +237,7 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin buffer.writeInt(maxConsumers); buffer.writeBoolean(purgeOnNoConsumers); buffer.writeByte(routingType); + buffer.writeBoolean(exclusive); } @Override @@ -224,7 +247,8 @@ public class PersistentQueueBindingEncoding implements EncodingSupport, QueueBin SimpleString.sizeofNullableString(createMetadata()) + DataConstants.SIZE_INT + DataConstants.SIZE_BOOLEAN + - DataConstants.SIZE_BYTE; + DataConstants.SIZE_BYTE + + DataConstants.SIZE_BOOLEAN; } private SimpleString createMetadata() { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java index 2a8764e62d..b78883fdb5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/PostOffice.java @@ -64,7 +64,8 @@ public interface PostOffice extends ActiveMQComponent { QueueBinding updateQueue(SimpleString name, RoutingType routingType, Integer maxConsumers, - Boolean purgeOnNoConsumers) throws Exception; + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception; List listQueuesForAddress(SimpleString address) throws Exception; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java index be4a0d51a6..9de5dcc354 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java @@ -464,7 +464,8 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding public QueueBinding updateQueue(SimpleString name, RoutingType routingType, Integer maxConsumers, - Boolean purgeOnNoConsumers) throws Exception { + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception { synchronized (addressLock) { final QueueBinding queueBinding = (QueueBinding) addressManager.getBinding(name); if (queueBinding == null) { @@ -504,6 +505,10 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding changed = true; queue.setPurgeOnNoConsumers(purgeOnNoConsumers); } + if (exclusive != null && queue.isExclusive() != exclusive.booleanValue()) { + changed = true; + queue.setExclusive(exclusive); + } if (changed) { final long txID = storageManager.generateID(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java index d47fbff00e..7b129ade5f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/protocol/core/ServerSessionPacketHandler.java @@ -353,7 +353,7 @@ public class ServerSessionPacketHandler implements ChannelHandler { CreateQueueMessage_V2 request = (CreateQueueMessage_V2) packet; requiresResponse = request.isRequiresResponse(); session.createQueue(request.getAddress(), request.getQueueName(), request.getRoutingType(), request.getFilterString(), request.isTemporary(), request.isDurable(), request.getMaxConsumers(), request.isPurgeOnNoConsumers(), - request.isAutoCreated()); + request.isExclusive(), request.isLastValue(), request.isAutoCreated()); if (requiresResponse) { response = new NullResponseMessage(); } @@ -371,7 +371,7 @@ public class ServerSessionPacketHandler implements ChannelHandler { case CREATE_SHARED_QUEUE_V2: { CreateSharedQueueMessage_V2 request = (CreateSharedQueueMessage_V2) packet; requiresResponse = request.isRequiresResponse(); - session.createSharedQueue(request.getAddress(), request.getQueueName(), request.getRoutingType(), request.isDurable(), request.getFilterString()); + session.createSharedQueue(request.getAddress(), request.getQueueName(), request.getRoutingType(), request.getFilterString(), request.isDurable(), request.getMaxConsumers(), request.isPurgeOnNoConsumers(), request.isExclusive(), request.isLastValue()); if (requiresResponse) { response = new NullResponseMessage(); } @@ -417,13 +417,13 @@ public class ServerSessionPacketHandler implements ChannelHandler { if (!queueNames.isEmpty()) { final List convertedQueueNames = request.convertQueueNames(clientVersion, queueNames); if (convertedQueueNames != queueNames) { - result = new BindingQueryResult(result.isExists(), result.getAddressInfo(), convertedQueueNames, result.isAutoCreateQueues(), result.isAutoCreateAddresses(), result.isDefaultPurgeOnNoConsumers(), result.getDefaultMaxConsumers()); + result = new BindingQueryResult(result.isExists(), result.getAddressInfo(), convertedQueueNames, result.isAutoCreateQueues(), result.isAutoCreateAddresses(), result.isDefaultPurgeOnNoConsumers(), result.getDefaultMaxConsumers(), result.isDefaultExclusive(), result.isDefaultLastValue()); } } } if (channel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V4)) { - response = new SessionBindingQueryResponseMessage_V4(result.isExists(), result.getQueueNames(), result.isAutoCreateQueues(), result.isAutoCreateAddresses(), result.isDefaultPurgeOnNoConsumers(), result.getDefaultMaxConsumers()); + response = new SessionBindingQueryResponseMessage_V4(result.isExists(), result.getQueueNames(), result.isAutoCreateQueues(), result.isAutoCreateAddresses(), result.isDefaultPurgeOnNoConsumers(), result.getDefaultMaxConsumers(), result.isDefaultExclusive(), result.isDefaultLastValue()); } else if (channel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V3)) { response = new SessionBindingQueryResponseMessage_V3(result.isExists(), result.getQueueNames(), result.isAutoCreateQueues(), result.isAutoCreateAddresses()); } else if (channel.supports(PacketImpl.SESS_BINDINGQUERY_RESP_V2)) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java index 6af94ff9c6..b8598b8825 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java @@ -333,6 +333,9 @@ public interface ActiveMQServer extends ServiceComponent { void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString name, SimpleString filterString, SimpleString user, boolean durable) throws Exception; + void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString name, SimpleString filterString, + SimpleString user, boolean durable, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, boolean temporary) throws Exception; @@ -351,10 +354,18 @@ public interface ActiveMQServer extends ServiceComponent { SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception; + Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, + Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception; + Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, + boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean exclusive, boolean lastValue, boolean autoCreateAddress) throws Exception; + @Deprecated Queue createQueue(SimpleString address, SimpleString queueName, SimpleString filter, boolean durable, boolean temporary) throws Exception; @@ -426,6 +437,12 @@ public interface ActiveMQServer extends ServiceComponent { Integer maxConsumers, Boolean purgeOnNoConsumers) throws Exception; + Queue updateQueue(String name, + RoutingType routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception; + /* * add a ProtocolManagerFactory to be used. Note if @see Configuration#isResolveProtocols is tur then this factory will * replace any factories with the same protocol diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/BindingQueryResult.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/BindingQueryResult.java index 7f340b1549..a76812c285 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/BindingQueryResult.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/BindingQueryResult.java @@ -37,13 +37,19 @@ public class BindingQueryResult { private final AddressInfo addressInfo; + private boolean defaultExclusive; + + private boolean defaultLastValue; + public BindingQueryResult(final boolean exists, final AddressInfo addressInfo, final List queueNames, final boolean autoCreateQueues, final boolean autoCreateAddresses, final boolean defaultPurgeOnNoConsumers, - final int defaultMaxConsumers) { + final int defaultMaxConsumers, + final boolean defaultExclusive, + final boolean defaultLastValue) { this.addressInfo = addressInfo; this.exists = exists; @@ -57,6 +63,10 @@ public class BindingQueryResult { this.defaultPurgeOnNoConsumers = defaultPurgeOnNoConsumers; this.defaultMaxConsumers = defaultMaxConsumers; + + this.defaultExclusive = defaultExclusive; + + this.defaultLastValue = defaultLastValue; } public boolean isExists() { @@ -86,4 +96,12 @@ public class BindingQueryResult { public int getDefaultMaxConsumers() { return defaultMaxConsumers; } + + public boolean isDefaultExclusive() { + return defaultExclusive; + } + + public boolean isDefaultLastValue() { + return defaultLastValue; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java index b39f4da608..ff4e82bd13 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/Queue.java @@ -68,6 +68,12 @@ public interface Queue extends Bindable,CriticalComponent { void setPurgeOnNoConsumers(boolean value); + boolean isExclusive(); + + void setExclusive(boolean value); + + boolean isLastValue(); + int getMaxConsumers(); void setMaxConsumer(int maxConsumers); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java index e879966601..75f859d409 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/QueueConfig.java @@ -37,6 +37,8 @@ public final class QueueConfig { private final boolean autoCreated; private final RoutingType routingType; private final int maxConsumers; + private final boolean exclusive; + private final boolean lastValue; private final boolean purgeOnNoConsumers; public static final class Builder { @@ -52,6 +54,8 @@ public final class QueueConfig { private boolean autoCreated; private RoutingType routingType; private int maxConsumers; + private boolean exclusive; + private boolean lastValue; private boolean purgeOnNoConsumers; private Builder(final long id, final SimpleString name) { @@ -70,6 +74,8 @@ public final class QueueConfig { this.autoCreated = true; this.routingType = ActiveMQDefaultConfiguration.getDefaultRoutingType(); this.maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(); + this.exclusive = ActiveMQDefaultConfiguration.getDefaultExclusive(); + this.lastValue = ActiveMQDefaultConfiguration.getDefaultLastValue(); this.purgeOnNoConsumers = ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(); validateState(); } @@ -122,6 +128,17 @@ public final class QueueConfig { return this; } + public Builder exclusive(final boolean exclusive) { + this.exclusive = exclusive; + return this; + } + + public Builder lastValue(final boolean lastValue) { + this.lastValue = lastValue; + return this; + } + + public Builder purgeOnNoConsumers(final boolean purgeOnNoConsumers) { this.purgeOnNoConsumers = purgeOnNoConsumers; return this; @@ -153,7 +170,7 @@ public final class QueueConfig { } else { pageSubscription = null; } - return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, purgeOnNoConsumers); + return new QueueConfig(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, lastValue, purgeOnNoConsumers); } } @@ -197,6 +214,8 @@ public final class QueueConfig { final boolean autoCreated, final RoutingType routingType, final int maxConsumers, + final boolean exclusive, + final boolean lastValue, final boolean purgeOnNoConsumers) { this.id = id; this.address = address; @@ -209,6 +228,8 @@ public final class QueueConfig { this.autoCreated = autoCreated; this.routingType = routingType; this.purgeOnNoConsumers = purgeOnNoConsumers; + this.exclusive = exclusive; + this.lastValue = lastValue; this.maxConsumers = maxConsumers; } @@ -256,6 +277,14 @@ public final class QueueConfig { return maxConsumers; } + public boolean isExclusive() { + return exclusive; + } + + public boolean isLastValue() { + return lastValue; + } + public RoutingType deliveryMode() { return routingType; } @@ -289,6 +318,10 @@ public final class QueueConfig { return false; if (maxConsumers != that.maxConsumers) return false; + if (exclusive != that.exclusive) + return false; + if (lastValue != that.lastValue) + return false; if (purgeOnNoConsumers != that.purgeOnNoConsumers) return false; return user != null ? user.equals(that.user) : that.user == null; @@ -308,6 +341,8 @@ public final class QueueConfig { result = 31 * result + (autoCreated ? 1 : 0); result = 31 * result + routingType.getType(); result = 31 * result + maxConsumers; + result = 31 * result + (exclusive ? 1 : 0); + result = 31 * result + (lastValue ? 1 : 0); result = 31 * result + (purgeOnNoConsumers ? 1 : 0); return result; } @@ -326,6 +361,8 @@ public final class QueueConfig { + ", autoCreated=" + autoCreated + ", routingType=" + routingType + ", maxConsumers=" + maxConsumers + + ", exclusive=" + exclusive + + ", lastValue=" + lastValue + ", purgeOnNoConsumers=" + purgeOnNoConsumers + '}'; } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java index d7e1ad2f7d..59a400bb72 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ServerSession.java @@ -149,6 +149,18 @@ public interface ServerSession extends SecurityAuth { boolean purgeOnNoConsumers, boolean autoCreated) throws Exception; + Queue createQueue(SimpleString address, + SimpleString name, + RoutingType routingType, + SimpleString filterString, + boolean temporary, + boolean durable, + int maxConsumers, + boolean purgeOnNoConsumers, + Boolean exclusive, + Boolean lastValue, + boolean autoCreated) throws Exception; + Queue createQueue(SimpleString address, SimpleString name, RoutingType routingType, @@ -164,6 +176,15 @@ public interface ServerSession extends SecurityAuth { boolean durable, boolean autoCreated) throws Exception; + Queue createQueue(AddressInfo addressInfo, + SimpleString name, + SimpleString filterString, + boolean temporary, + boolean durable, + Boolean exclusive, + Boolean lastValue, + boolean autoCreated) throws Exception; + AddressInfo createAddress(SimpleString address, EnumSet routingTypes, boolean autoCreated) throws Exception; @@ -253,6 +274,16 @@ public interface ServerSession extends SecurityAuth { boolean isClosed(); + void createSharedQueue(SimpleString address, + SimpleString name, + RoutingType routingType, + SimpleString filterString, + boolean durable, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Boolean lastValue) throws Exception; + void createSharedQueue(SimpleString address, SimpleString name, RoutingType routingType, diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java index 6ce681e304..d7eb2b9e55 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java @@ -825,6 +825,8 @@ public class ActiveMQServerImpl implements ActiveMQServer { boolean autoCreateAddresses = addressSettings.isAutoCreateAddresses(); boolean defaultPurgeOnNoConsumers = addressSettings.isDefaultPurgeOnNoConsumers(); int defaultMaxConsumers = addressSettings.getDefaultMaxConsumers(); + boolean defaultExclusive = addressSettings.isDefaultExclusiveQueue(); + boolean defaultLastValie = addressSettings.isDefaultLastValueQueue(); List names = new ArrayList<>(); @@ -833,7 +835,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { SimpleString bindAddress = new SimpleString(realAddress); if (managementService != null) { if (bindAddress.equals(managementService.getManagementAddress())) { - return new BindingQueryResult(true, null, names, autoCreateQeueus, autoCreateAddresses, defaultPurgeOnNoConsumers, defaultMaxConsumers); + return new BindingQueryResult(true, null, names, autoCreateQeueus, autoCreateAddresses, defaultPurgeOnNoConsumers, defaultMaxConsumers, defaultExclusive, defaultLastValie); } } @@ -851,7 +853,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { AddressInfo info = getAddressInfo(bindAddress); - return new BindingQueryResult(info != null, info, names, autoCreateQeueus, autoCreateAddresses, defaultPurgeOnNoConsumers, defaultMaxConsumers); + return new BindingQueryResult(info != null, info, names, autoCreateQeueus, autoCreateAddresses, defaultPurgeOnNoConsumers, defaultMaxConsumers, defaultExclusive, defaultLastValie); } @Override @@ -863,6 +865,8 @@ public class ActiveMQServerImpl implements ActiveMQServer { boolean autoCreateQueues = getAddressSettingsRepository().getMatch(name.toString()).isAutoCreateQueues(); boolean defaultPurgeOnNoConsumers = getAddressSettingsRepository().getMatch(name.toString()).isDefaultPurgeOnNoConsumers(); int defaultMaxConsumers = getAddressSettingsRepository().getMatch(name.toString()).getDefaultMaxConsumers(); + boolean defaultExclusiveQueue = getAddressSettingsRepository().getMatch(name.toString()).isDefaultExclusiveQueue(); + boolean defaultLastValueQueue = getAddressSettingsRepository().getMatch(name.toString()).isDefaultLastValueQueue(); QueueQueryResult response; @@ -877,14 +881,14 @@ public class ActiveMQServerImpl implements ActiveMQServer { SimpleString filterString = filter == null ? null : filter.getFilterString(); - response = new QueueQueryResult(name, binding.getAddress(), queue.isDurable(), queue.isTemporary(), filterString, queue.getConsumerCount(), queue.getMessageCount(), autoCreateQueues, true, queue.isAutoCreated(), queue.isPurgeOnNoConsumers(), queue.getRoutingType(), queue.getMaxConsumers()); + response = new QueueQueryResult(name, binding.getAddress(), queue.isDurable(), queue.isTemporary(), filterString, queue.getConsumerCount(), queue.getMessageCount(), autoCreateQueues, true, queue.isAutoCreated(), queue.isPurgeOnNoConsumers(), queue.getRoutingType(), queue.getMaxConsumers(), queue.isExclusive(), queue.isLastValue()); } else if (name.equals(managementAddress)) { // make an exception for the management address (see HORNETQ-29) - response = new QueueQueryResult(name, managementAddress, true, false, null, -1, -1, autoCreateQueues, true, false, false, RoutingType.MULTICAST, -1); + response = new QueueQueryResult(name, managementAddress, true, false, null, -1, -1, autoCreateQueues, true, false, false, RoutingType.MULTICAST, -1, false, false); } else if (autoCreateQueues) { - response = new QueueQueryResult(name, name, true, false, null, 0, 0, true, false, false, defaultPurgeOnNoConsumers, RoutingType.MULTICAST, defaultMaxConsumers); + response = new QueueQueryResult(name, name, true, false, null, 0, 0, true, false, false, defaultPurgeOnNoConsumers, RoutingType.MULTICAST, defaultMaxConsumers, defaultExclusiveQueue, defaultLastValueQueue); } else { - response = new QueueQueryResult(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, 0); + response = new QueueQueryResult(null, null, false, false, null, 0, 0, false, false, false, false, RoutingType.MULTICAST, 0, null, null); } return response; @@ -1672,9 +1676,25 @@ public class ActiveMQServerImpl implements ActiveMQServer { @Override public Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { - return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, autoCreateAddress); + AddressSettings as = getAddressSettingsRepository().getMatch(addressInfo == null ? queueName.toString() : addressInfo.getName().toString()); + return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreateAddress); } + @Override + public Queue createQueue(AddressInfo addressInfo, SimpleString queueName, SimpleString filter, SimpleString user, boolean durable, boolean temporary, boolean autoCreated, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue, boolean autoCreateAddress) throws Exception { + return createQueue(addressInfo, queueName, filter, user, durable, temporary, false, false, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, autoCreateAddress); + } + + + @Override + public Queue createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + SimpleString user, boolean durable, boolean temporary, boolean ignoreIfExists, boolean transientQueue, + boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, boolean autoCreateAddress) throws Exception { + AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? queueName.toString() : address.toString()); + return createQueue(address, routingType, queueName, filter, user, durable, temporary, ignoreIfExists, transientQueue, autoCreated, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreateAddress); + } + + @Deprecated @Override public Queue createQueue(final SimpleString address, @@ -1692,6 +1712,21 @@ public class ActiveMQServerImpl implements ActiveMQServer { final SimpleString filterString, final SimpleString user, boolean durable) throws Exception { + AddressSettings as = getAddressSettingsRepository().getMatch(address == null ? name.toString() : address.toString()); + createSharedQueue(address, routingType, name, filterString, user, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue()); + } + + @Override + public void createSharedQueue(final SimpleString address, + RoutingType routingType, + final SimpleString name, + final SimpleString filterString, + final SimpleString user, + boolean durable, + int maxConsumers, + boolean purgeOnNoConsumers, + boolean exclusive, + boolean lastValue) throws Exception { //force the old contract about address if (address == null) { throw new NullPointerException("address can't be null!"); @@ -1705,7 +1740,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { } } - final Queue queue = createQueue(address, routingType, name, filterString, user, durable, !durable, true, !durable, false, Queue.MAX_CONSUMERS_UNLIMITED, false, true); + final Queue queue = createQueue(address, routingType, name, filterString, user, durable, !durable, true, !durable, false, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, true); if (!queue.getAddress().equals(address)) { throw ActiveMQMessageBundle.BUNDLE.queueSubscriptionBelongsToDifferentAddress(name); @@ -2480,16 +2515,19 @@ public class ActiveMQServerImpl implements ActiveMQServer { for (CoreQueueConfiguration config : queues) { SimpleString queueName = SimpleString.toSimpleString(config.getName()); ActiveMQServerLogger.LOGGER.deployQueue(config.getName(), config.getAddress()); - + AddressSettings as = addressSettingsRepository.getMatch(config.getAddress()); // determine if there is an address::queue match; update it if so if (locateQueue(queueName) != null && locateQueue(queueName).getAddress().toString().equals(config.getAddress())) { - updateQueue(config.getName(), config.getRoutingType(), config.getMaxConsumers(), config.getPurgeOnNoConsumers()); + updateQueue(config.getName(), config.getRoutingType(), config.getMaxConsumers(), config.getPurgeOnNoConsumers(), + config.isExclusive() == null ? as.isDefaultExclusiveQueue() : config.isExclusive()); } else { // if the address::queue doesn't exist then create it try { createQueue(SimpleString.toSimpleString(config.getAddress()), config.getRoutingType(), queueName, SimpleString.toSimpleString(config.getFilterString()), SimpleString.toSimpleString(config.getUser()), - config.isDurable(),false,false,false,false,config.getMaxConsumers(),config.getPurgeOnNoConsumers(),true); + config.isDurable(),false,false,false,false,config.getMaxConsumers(),config.getPurgeOnNoConsumers(), + config.isExclusive() == null ? as.isDefaultExclusiveQueue() : config.isExclusive(), + config.isLastValue() == null ? as.isDefaultLastValueQueue() : config.isLastValue(), true); } catch (ActiveMQQueueExistsException e) { // the queue may exist on a *different* address ActiveMQServerLogger.LOGGER.warn(e.getMessage()); @@ -2668,6 +2706,8 @@ public class ActiveMQServerImpl implements ActiveMQServer { final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers, + final boolean exclusive, + final boolean lastValue, final boolean autoCreateAddress) throws Exception { final QueueBinding binding = (QueueBinding) postOffice.getBinding(queueName); if (binding != null) { @@ -2710,7 +2750,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { throw ActiveMQMessageBundle.BUNDLE.invalidRoutingTypeForAddress(rt, info.getName().toString(), info.getRoutingTypes()); } - final QueueConfig queueConfig = queueConfigBuilder.filter(filter).pagingManager(pagingManager).user(user).durable(durable).temporary(temporary).autoCreated(autoCreated).routingType(addrInfo.getRoutingType()).maxConsumers(maxConsumers).purgeOnNoConsumers(purgeOnNoConsumers).build(); + final QueueConfig queueConfig = queueConfigBuilder.filter(filter).pagingManager(pagingManager).user(user).durable(durable).temporary(temporary).autoCreated(autoCreated).routingType(addrInfo.getRoutingType()).maxConsumers(maxConsumers).purgeOnNoConsumers(purgeOnNoConsumers).exclusive(exclusive).lastValue(lastValue).build(); callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateQueue(queueConfig) : null); @@ -2776,6 +2816,8 @@ public class ActiveMQServerImpl implements ActiveMQServer { final boolean autoCreated, final int maxConsumers, final boolean purgeOnNoConsumers, + final boolean exclusive, + final boolean lastValue, final boolean autoCreateAddress) throws Exception { final QueueBinding binding = (QueueBinding) postOffice.getBinding(queueName); @@ -2817,7 +2859,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { throw ActiveMQMessageBundle.BUNDLE.invalidRoutingTypeForAddress(routingType, info.getName().toString(), info.getRoutingTypes()); } - final QueueConfig queueConfig = queueConfigBuilder.filter(filter).pagingManager(pagingManager).user(user).durable(durable).temporary(temporary).autoCreated(autoCreated).routingType(routingType).maxConsumers(maxConsumers).purgeOnNoConsumers(purgeOnNoConsumers).build(); + final QueueConfig queueConfig = queueConfigBuilder.filter(filter).pagingManager(pagingManager).user(user).durable(durable).temporary(temporary).autoCreated(autoCreated).routingType(routingType).maxConsumers(maxConsumers).purgeOnNoConsumers(purgeOnNoConsumers).exclusive(exclusive).lastValue(lastValue).build(); callBrokerPlugins(hasBrokerPlugins() ? plugin -> plugin.beforeCreateQueue(queueConfig) : null); @@ -2873,8 +2915,16 @@ public class ActiveMQServerImpl implements ActiveMQServer { RoutingType routingType, Integer maxConsumers, Boolean purgeOnNoConsumers) throws Exception { + return updateQueue(name, routingType, maxConsumers, purgeOnNoConsumers, null); + } - final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers); + @Override + public Queue updateQueue(String name, + RoutingType routingType, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception { + final QueueBinding queueBinding = this.postOffice.updateQueue(new SimpleString(name), routingType, maxConsumers, purgeOnNoConsumers, exclusive); if (queueBinding != null) { final Queue queue = queueBinding.getQueue(); return queue; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java index 5951a01675..7aada5e881 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LastValueQueue.java @@ -60,6 +60,7 @@ public class LastValueQueue extends QueueImpl { final boolean autoCreated, final RoutingType routingType, final Integer maxConsumers, + final Boolean exclusive, final Boolean purgeOnNoConsumers, final ScheduledExecutorService scheduledExecutor, final PostOffice postOffice, @@ -68,7 +69,7 @@ public class LastValueQueue extends QueueImpl { final ArtemisExecutor executor, final ActiveMQServer server, final QueueFactory factory) { - super(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); + super(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, exclusive, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); } @Override @@ -162,6 +163,11 @@ public class LastValueQueue extends QueueImpl { super.refRemoved(ref); } + @Override + public boolean isLastValue() { + return true; + } + private class HolderReference implements MessageReference { private final SimpleString prop; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java index 96b6662b06..cfd06d991f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/PostOfficeJournalLoader.java @@ -150,6 +150,7 @@ public class PostOfficeJournalLoader implements JournalLoader { .autoCreated(queueBindingInfo.isAutoCreated()) .purgeOnNoConsumers(queueBindingInfo.isPurgeOnNoConsumers()) .maxConsumers(queueBindingInfo.getMaxConsumers()) + .exclusive(queueBindingInfo.isExclusive()) .routingType(RoutingType.getType(queueBindingInfo.getRoutingType())); final Queue queue = queueFactory.createQueueWith(queueConfigBuilder.build()); queue.setConsumersRefCount(new QueueManagerImpl(((PostOfficeImpl)postOffice).getServer(), queueBindingInfo.getQueueName())); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java index 64da8b02af..7f23d099df 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueFactoryImpl.java @@ -72,12 +72,11 @@ public class QueueFactoryImpl implements QueueFactory { @Override public Queue createQueueWith(final QueueConfig config) { - final AddressSettings addressSettings = addressSettingsRepository.getMatch(config.address().toString()); final Queue queue; - if (addressSettings.isLastValueQueue()) { - queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + if (config.isLastValue()) { + queue = new LastValueQueue(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { - queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + queue = new QueueImpl(config.id(), config.address(), config.name(), config.filter(), config.pageSubscription(), config.user(), config.isDurable(), config.isTemporary(), config.isAutoCreated(), config.deliveryMode(), config.maxConsumers(), config.isExclusive(), config.isPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } server.getCriticalAnalyzer().add(queue); @@ -102,8 +101,8 @@ public class QueueFactoryImpl implements QueueFactory { AddressSettings addressSettings = addressSettingsRepository.getMatch(address.toString()); Queue queue; - if (addressSettings.isLastValueQueue()) { - queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); + if (addressSettings.isDefaultLastValueQueue()) { + queue = new LastValueQueue(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, ActiveMQDefaultConfiguration.getDefaultRoutingType(), ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers(), ActiveMQDefaultConfiguration.getDefaultExclusive(), ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } else { queue = new QueueImpl(persistenceID, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executorFactory.getExecutor(), server, this); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java index c527c46cf6..dbf79e25bf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/QueueImpl.java @@ -257,6 +257,8 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { private volatile int maxConsumers; + private volatile boolean exclusive; + private volatile boolean purgeOnNoConsumers; private final AddressInfo addressInfo; @@ -389,6 +391,29 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { final ArtemisExecutor executor, final ActiveMQServer server, final QueueFactory factory) { + this(id, address, name, filter, pageSubscription, user, durable, temporary, autoCreated, routingType, maxConsumers, null, purgeOnNoConsumers, scheduledExecutor, postOffice, storageManager, addressSettingsRepository, executor, server, factory); + } + + public QueueImpl(final long id, + final SimpleString address, + final SimpleString name, + final Filter filter, + final PageSubscription pageSubscription, + final SimpleString user, + final boolean durable, + final boolean temporary, + final boolean autoCreated, + final RoutingType routingType, + final Integer maxConsumers, + final Boolean exclusive, + final Boolean purgeOnNoConsumers, + final ScheduledExecutorService scheduledExecutor, + final PostOffice postOffice, + final StorageManager storageManager, + final HierarchicalRepository addressSettingsRepository, + final ArtemisExecutor executor, + final ActiveMQServer server, + final QueueFactory factory) { super(server == null ? EmptyCriticalAnalyzer.getInstance() : server.getCriticalAnalyzer(), CRITICAL_PATHS); this.id = id; @@ -413,6 +438,8 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { this.maxConsumers = maxConsumers == null ? ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers() : maxConsumers; + this.exclusive = exclusive == null ? ActiveMQDefaultConfiguration.getDefaultExclusive() : exclusive; + this.purgeOnNoConsumers = purgeOnNoConsumers == null ? ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers() : purgeOnNoConsumers; this.postOffice = postOffice; @@ -463,7 +490,18 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { return user; } + @Override public boolean isExclusive() { + return exclusive; + } + + @Override + public synchronized void setExclusive(boolean exclusive) { + this.exclusive = exclusive; + } + + @Override + public boolean isLastValue() { return false; } @@ -2190,6 +2228,10 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { } } + if (exclusive) { + consumer = consumerList.get(0).consumer; + } + HandleStatus status = handle(ref, consumer); if (status == HandleStatus.HANDLED) { @@ -2237,7 +2279,7 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { // Only move onto the next position if the consumer on the current position was used. // When using group we don't need to load balance to the next position - if (groupConsumer == null) { + if (!exclusive && groupConsumer == null) { pos++; } @@ -2730,8 +2772,12 @@ public class QueueImpl extends CriticalComponentImpl implements Queue { } } + if (exclusive) { + consumer = consumerList.get(0).consumer; + } + // Only move onto the next position if the consumer on the current position was used. - if (groupConsumer == null) { + if (!exclusive && groupConsumer == null) { pos++; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index a3e817b7f6..55125bde33 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -559,7 +559,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener { @Override public Queue createQueue(AddressInfo addressInfo, SimpleString name, SimpleString filterString, boolean temporary, boolean durable) throws Exception { AddressSettings as = server.getAddressSettingsRepository().getMatch(addressInfo.getName().toString()); - return createQueue(addressInfo, name, filterString, temporary, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), false); + return createQueue(addressInfo, name, filterString, temporary, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), false); } public Queue createQueue(final AddressInfo addressInfo, @@ -569,6 +569,8 @@ public class ServerSessionImpl implements ServerSession, FailureListener { final boolean durable, final int maxConsumers, final boolean purgeOnNoConsumers, + final boolean exclusive, + final boolean lastValue, final boolean autoCreated) throws Exception { final SimpleString unPrefixedName = removePrefix(name); @@ -583,7 +585,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener { server.checkQueueCreationLimit(getUsername()); - Queue queue = server.createQueue(art, unPrefixedName, filterString, SimpleString.toSimpleString(getUsername()), durable, temporary, autoCreated, maxConsumers, purgeOnNoConsumers, server.getAddressSettingsRepository().getMatch(art.getName().toString()).isAutoCreateAddresses()); + Queue queue = server.createQueue(art, unPrefixedName, filterString, SimpleString.toSimpleString(getUsername()), durable, temporary, autoCreated, maxConsumers, purgeOnNoConsumers, exclusive, lastValue, server.getAddressSettingsRepository().getMatch(art.getName().toString()).isAutoCreateAddresses()); if (temporary) { // Temporary queue in core simply means the queue will be deleted if @@ -622,7 +624,30 @@ public class ServerSessionImpl implements ServerSession, FailureListener { final int maxConsumers, final boolean purgeOnNoConsumers, final boolean autoCreated) throws Exception { - return createQueue(new AddressInfo(address, routingType), name, filterString, temporary, durable, maxConsumers, purgeOnNoConsumers, autoCreated); + AddressSettings as = server.getAddressSettingsRepository().getMatch(address.toString()); + return createQueue(new AddressInfo(address, routingType), name, filterString, temporary, durable, maxConsumers, purgeOnNoConsumers, as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreated); + } + + @Override + public Queue createQueue(final SimpleString address, + final SimpleString name, + final RoutingType routingType, + final SimpleString filterString, + final boolean temporary, + final boolean durable, + final int maxConsumers, + final boolean purgeOnNoConsumers, + final Boolean exclusive, + final Boolean lastValue, + final boolean autoCreated) throws Exception { + if (exclusive == null || lastValue == null) { + AddressSettings as = server.getAddressSettingsRepository().getMatch(address.toString()); + return createQueue(new AddressInfo(address, routingType), name, filterString, temporary, durable, maxConsumers, purgeOnNoConsumers, + exclusive == null ? as.isDefaultExclusiveQueue() : exclusive, lastValue == null ? as.isDefaultLastValueQueue() : lastValue, autoCreated); + } else { + return createQueue(new AddressInfo(address, routingType), name, filterString, temporary, durable, maxConsumers, purgeOnNoConsumers, + exclusive, lastValue, autoCreated); + } } @Override @@ -640,7 +665,14 @@ public class ServerSessionImpl implements ServerSession, FailureListener { @Override public Queue createQueue(AddressInfo addressInfo, SimpleString name, SimpleString filterString, boolean temporary, boolean durable, boolean autoCreated) throws Exception { AddressSettings as = server.getAddressSettingsRepository().getMatch(addressInfo.getName().toString()); - return createQueue(addressInfo, name, filterString, temporary, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), autoCreated); + return createQueue(addressInfo, name, filterString, temporary, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), as.isDefaultExclusiveQueue(), as.isDefaultLastValueQueue(), autoCreated); + } + + @Override + public Queue createQueue(AddressInfo addressInfo, SimpleString name, SimpleString filterString, boolean temporary, boolean durable, Boolean exclusive, Boolean lastValue, boolean autoCreated) throws Exception { + AddressSettings as = server.getAddressSettingsRepository().getMatch(addressInfo.getName().toString()); + return createQueue(addressInfo, name, filterString, temporary, durable, as.getDefaultMaxConsumers(), as.isDefaultPurgeOnNoConsumers(), + exclusive == null ? as.isDefaultExclusiveQueue() : exclusive, lastValue == null ? as.isDefaultLastValueQueue() : lastValue, autoCreated); } @Override @@ -668,6 +700,31 @@ public class ServerSessionImpl implements ServerSession, FailureListener { return server.getAddressInfo(art.getName()); } + @Override + public void createSharedQueue(SimpleString address, + SimpleString name, + RoutingType routingType, + SimpleString filterString, + boolean durable, + Integer maxConsumers, + Boolean purgeOnNoConsumers, + Boolean exclusive, + Boolean lastValue) throws Exception { + address = removePrefix(address); + + securityCheck(address, name, durable ? CheckType.CREATE_DURABLE_QUEUE : CheckType.CREATE_NON_DURABLE_QUEUE, this); + + server.checkQueueCreationLimit(getUsername()); + + AddressSettings as = server.getAddressSettingsRepository().getMatch(address.toString()); + + server.createSharedQueue(address, routingType, name, filterString, SimpleString.toSimpleString(getUsername()), durable, + maxConsumers == null ? as.getDefaultMaxConsumers() : maxConsumers, + purgeOnNoConsumers == null ? as.isDefaultPurgeOnNoConsumers() : purgeOnNoConsumers, + exclusive == null ? as.isDefaultExclusiveQueue() : exclusive, + lastValue == null ? as.isDefaultLastValueQueue() : lastValue); + } + @Override public void createSharedQueue(SimpleString address, final SimpleString name, @@ -675,13 +732,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener { boolean durable, final SimpleString filterString) throws Exception { - address = removePrefix(address); - - securityCheck(address, name, CheckType.CREATE_NON_DURABLE_QUEUE, this); - - server.checkQueueCreationLimit(getUsername()); - - server.createSharedQueue(address, routingType, name, filterString, SimpleString.toSimpleString(getUsername()), durable); + createSharedQueue(address, name, routingType, filterString, durable, null, null, null, null); } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java index ca9c550660..c7021fdc4f 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/AddressSettings.java @@ -124,7 +124,9 @@ public class AddressSettings implements Mergeable, Serializable private Long expiryDelay = AddressSettings.DEFAULT_EXPIRY_DELAY; - private Boolean lastValueQueue = null; + private Boolean defaultLastValueQueue = null; + + private Boolean defaultExclusiveQueue = null; private Long redistributionDelay = null; @@ -190,7 +192,8 @@ public class AddressSettings implements Mergeable, Serializable this.deadLetterAddress = other.deadLetterAddress; this.expiryAddress = other.expiryAddress; this.expiryDelay = other.expiryDelay; - this.lastValueQueue = other.lastValueQueue; + this.defaultLastValueQueue = other.defaultLastValueQueue; + this.defaultExclusiveQueue = other.defaultExclusiveQueue; this.redistributionDelay = other.redistributionDelay; this.sendToDLAOnNoRoute = other.sendToDLAOnNoRoute; this.slowConsumerThreshold = other.slowConsumerThreshold; @@ -352,12 +355,21 @@ public class AddressSettings implements Mergeable, Serializable return this; } - public boolean isLastValueQueue() { - return lastValueQueue != null ? lastValueQueue : AddressSettings.DEFAULT_LAST_VALUE_QUEUE; + public boolean isDefaultLastValueQueue() { + return defaultLastValueQueue != null ? defaultLastValueQueue : AddressSettings.DEFAULT_LAST_VALUE_QUEUE; } - public AddressSettings setLastValueQueue(final boolean lastValueQueue) { - this.lastValueQueue = lastValueQueue; + public AddressSettings setDefaultLastValueQueue(final boolean defaultLastValueQueue) { + this.defaultLastValueQueue = defaultLastValueQueue; + return this; + } + + public boolean isDefaultExclusiveQueue() { + return defaultExclusiveQueue != null ? defaultExclusiveQueue : ActiveMQDefaultConfiguration.getDefaultExclusive(); + } + + public AddressSettings setDefaultExclusiveQueue(Boolean defaultExclusiveQueue) { + this.defaultExclusiveQueue = defaultExclusiveQueue; return this; } @@ -691,7 +703,7 @@ public class AddressSettings implements Mergeable, Serializable expiryDelay = BufferHelper.readNullableLong(buffer); - lastValueQueue = BufferHelper.readNullableBoolean(buffer); + defaultLastValueQueue = BufferHelper.readNullableBoolean(buffer); redistributionDelay = BufferHelper.readNullableLong(buffer); @@ -751,6 +763,10 @@ public class AddressSettings implements Mergeable, Serializable defaultQueueRoutingType = RoutingType.getType(buffer.readByte()); defaultAddressRoutingType = RoutingType.getType(buffer.readByte()); + + if (buffer.readableBytes() > 0) { + defaultExclusiveQueue = BufferHelper.readNullableBoolean(buffer); + } } @Override @@ -769,7 +785,7 @@ public class AddressSettings implements Mergeable, Serializable SimpleString.sizeofNullableString(deadLetterAddress) + SimpleString.sizeofNullableString(expiryAddress) + BufferHelper.sizeOfNullableLong(expiryDelay) + - BufferHelper.sizeOfNullableBoolean(lastValueQueue) + + BufferHelper.sizeOfNullableBoolean(defaultLastValueQueue) + BufferHelper.sizeOfNullableLong(redistributionDelay) + BufferHelper.sizeOfNullableBoolean(sendToDLAOnNoRoute) + BufferHelper.sizeOfNullableLong(slowConsumerCheckPeriod) + @@ -788,7 +804,8 @@ public class AddressSettings implements Mergeable, Serializable BufferHelper.sizeOfNullableInteger(defaultMaxConsumers) + BufferHelper.sizeOfNullableBoolean(defaultPurgeOnNoConsumers) + DataConstants.SIZE_BYTE + - DataConstants.SIZE_BYTE; + DataConstants.SIZE_BYTE + + BufferHelper.sizeOfNullableBoolean(defaultExclusiveQueue); } @Override @@ -819,7 +836,7 @@ public class AddressSettings implements Mergeable, Serializable BufferHelper.writeNullableLong(buffer, expiryDelay); - BufferHelper.writeNullableBoolean(buffer, lastValueQueue); + BufferHelper.writeNullableBoolean(buffer, defaultLastValueQueue); BufferHelper.writeNullableLong(buffer, redistributionDelay); @@ -862,6 +879,9 @@ public class AddressSettings implements Mergeable, Serializable buffer.writeByte(defaultQueueRoutingType == null ? -1 : defaultQueueRoutingType.getType()); buffer.writeByte(defaultAddressRoutingType == null ? -1 : defaultAddressRoutingType.getType()); + + BufferHelper.writeNullableBoolean(buffer, defaultExclusiveQueue); + } /* (non-Javadoc) @@ -876,7 +896,8 @@ public class AddressSettings implements Mergeable, Serializable result = prime * result + ((dropMessagesWhenFull == null) ? 0 : dropMessagesWhenFull.hashCode()); result = prime * result + ((expiryAddress == null) ? 0 : expiryAddress.hashCode()); result = prime * result + ((expiryDelay == null) ? 0 : expiryDelay.hashCode()); - result = prime * result + ((lastValueQueue == null) ? 0 : lastValueQueue.hashCode()); + result = prime * result + ((defaultLastValueQueue == null) ? 0 : defaultLastValueQueue.hashCode()); + result = prime * result + ((defaultExclusiveQueue == null) ? 0 : defaultExclusiveQueue.hashCode()); result = prime * result + ((maxDeliveryAttempts == null) ? 0 : maxDeliveryAttempts.hashCode()); result = prime * result + ((maxSizeBytes == null) ? 0 : maxSizeBytes.hashCode()); result = prime * result + ((messageCounterHistoryDayLimit == null) ? 0 : messageCounterHistoryDayLimit.hashCode()); @@ -947,10 +968,15 @@ public class AddressSettings implements Mergeable, Serializable return false; } else if (!expiryDelay.equals(other.expiryDelay)) return false; - if (lastValueQueue == null) { - if (other.lastValueQueue != null) + if (defaultLastValueQueue == null) { + if (other.defaultLastValueQueue != null) return false; - } else if (!lastValueQueue.equals(other.lastValueQueue)) + } else if (!defaultLastValueQueue.equals(other.defaultLastValueQueue)) + return false; + if (defaultExclusiveQueue == null) { + if (other.defaultExclusiveQueue != null) + return false; + } else if (!defaultExclusiveQueue.equals(other.defaultExclusiveQueue)) return false; if (maxDeliveryAttempts == null) { if (other.maxDeliveryAttempts != null) @@ -1124,8 +1150,10 @@ public class AddressSettings implements Mergeable, Serializable expiryAddress + ", expiryDelay=" + expiryDelay + - ", lastValueQueue=" + - lastValueQueue + + ", defaultLastValueQueue=" + + defaultLastValueQueue + + ", defaultExclusiveQueue=" + + defaultExclusiveQueue + ", maxDeliveryAttempts=" + maxDeliveryAttempts + ", maxSizeBytes=" + diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 58abd67f5c..f67cd48d9a 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -502,6 +502,8 @@ + + @@ -2693,7 +2695,23 @@ - whether to treat the queue as a last value queue + This is deprecated please use default-last-value-queue instead. + + + + + + + + whether to treat the queues under the address as a last value queues by default + + + + + + + + whether to treat the queues under the address as exclusive queues by default @@ -3009,6 +3027,8 @@ + + diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java index 256a670cbe..4fda8b39df 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/impl/ScheduledDeliveryHandlerTest.java @@ -1311,6 +1311,21 @@ public class ScheduledDeliveryHandlerTest extends Assert { return null; } + @Override + public boolean isLastValue() { + return false; + } + + @Override + public boolean isExclusive() { + return false; + } + + @Override + public void setExclusive(boolean exclusive) { + + } + @Override public void decDelivering(int size) { diff --git a/artemis-tools/src/test/resources/artemis-configuration.xsd b/artemis-tools/src/test/resources/artemis-configuration.xsd index c0b88f9ecc..cb579a8f3d 100644 --- a/artemis-tools/src/test/resources/artemis-configuration.xsd +++ b/artemis-tools/src/test/resources/artemis-configuration.xsd @@ -484,6 +484,8 @@ + + @@ -2422,7 +2424,23 @@ - whether to treat the queue as a last value queue + This is deprecated please use default-last-value-queue instead. + + + + + + + + whether to treat the queues under the address as a last value queues by default + + + + + + + + whether to treat the queues under the address as exclusive queues by default @@ -2679,6 +2697,7 @@ + @@ -2691,6 +2710,8 @@ + + diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSLVQTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSLVQTest.java index 0ffa9c5567..1c504122cd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSLVQTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSLVQTest.java @@ -43,7 +43,7 @@ public class JMSLVQTest extends JMSClientTestSupport { @Override protected void addConfiguration(ActiveMQServer server) { - server.getAddressSettingsRepository().addMatch(LVQ_QUEUE_NAME, new AddressSettings().setLastValueQueue(true)); + server.getAddressSettingsRepository().addMatch(LVQ_QUEUE_NAME, new AddressSettings().setDefaultLastValueQueue(true)); } @Override protected void createAddressAndQueues(ActiveMQServer server) throws Exception { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/MultipleProducersTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/MultipleProducersTest.java index f41242696c..0c9c5663e5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/MultipleProducersTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/MultipleProducersTest.java @@ -74,7 +74,7 @@ public class MultipleProducersTest extends JMSTestBase { addressSettings.setDeadLetterAddress(expiryQueue); addressSettings.setRedeliveryDelay(0); addressSettings.setMessageCounterHistoryDayLimit(2); - addressSettings.setLastValueQueue(false); + addressSettings.setDefaultLastValueQueue(false); addressSettings.setMaxDeliveryAttempts(10); addressSettings.setMaxSizeBytes(1048576); addressSettings.setPageCacheMaxSize(5); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionCreateAndDeleteQueueTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionCreateAndDeleteQueueTest.java index 4d6dde3c3e..af4ab8db97 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionCreateAndDeleteQueueTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SessionCreateAndDeleteQueueTest.java @@ -99,7 +99,7 @@ public class SessionCreateAndDeleteQueueTest extends ActiveMQTestBase { @Test public void testAddressSettingUSed() throws Exception { - server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setLastValueQueue(true)); + server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setDefaultLastValueQueue(true)); ClientSession session = createSessionFactory(locator).createSession(false, true, true); SimpleString filterString = new SimpleString("x=y"); session.createQueue(address, queueName, filterString, false); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java index 5d1763f5e6..f5b6a001f9 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/UpdateQueueTest.java @@ -59,7 +59,7 @@ public class UpdateQueueTest extends ActiveMQTestBase { prod.send(session.createTextMessage("message " + i)); } - server.updateQueue(ADDRESS.toString(), RoutingType.ANYCAST, 1, false); + server.updateQueue(ADDRESS.toString(), RoutingType.ANYCAST, 1, false, false); conn.close(); factory.close(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExclusiveTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExclusiveTest.java new file mode 100644 index 0000000000..1331615623 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/ExclusiveTest.java @@ -0,0 +1,319 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.tests.integration.jms.client; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSConsumer; +import javax.jms.JMSContext; +import javax.jms.JMSProducer; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.jms.client.ActiveMQDestination; +import org.apache.activemq.artemis.tests.util.JMSTestBase; +import org.junit.Before; +import org.junit.Test; + +/** + * Exclusive Test + */ +public class ExclusiveTest extends JMSTestBase { + + private SimpleString queueName = SimpleString.toSimpleString("jms.exclusive.queue"); + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + server.createQueue(queueName, RoutingType.ANYCAST, queueName, null, null, true, false, false, false, false, -1, false, true, false,true); + } + + + protected ConnectionFactory getCF() throws Exception { + return cf; + } + + @Test + public void testExclusiveQueueConsumer() throws Exception { + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Destination queue = session.createQueue(queueName.toString()); + MessageProducer producer = session.createProducer(queue); + + MessageConsumer consumer1 = session.createConsumer(queue); + MessageConsumer consumer2 = session.createConsumer(queue); + MessageConsumer consumer3 = session.createConsumer(queue); + + connection.start(); + + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + + producer.send(message); + } + + + //All msgs should go to the first consumer + for (int j = 0; j < 100; j++) { + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer2.receiveNoWait(); + assertNull(tm); + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + } finally { + connection.close(); + } + } + + @Test + public void testExclusiveWithJMS2Producer() throws Exception { + ConnectionFactory fact = getCF(); + JMSContext ctx = addContext(getCF().createContext(JMSContext.SESSION_TRANSACTED)); + + try { + JMSProducer producer = ctx.createProducer(); + Destination queue = ctx.createQueue(queueName.toString()); + + JMSConsumer consumer1 = ctx.createConsumer(queue); + JMSConsumer consumer2 = ctx.createConsumer(queue); + JMSConsumer consumer3 = ctx.createConsumer(queue); + + ctx.start(); + + for (int j = 0; j < 100; j++) { + TextMessage message = ctx.createTextMessage("Message" + j); + + producer.send(queue, message); + } + + ctx.commit(); + + //All msgs should go to the first consumer + for (int j = 0; j < 100; j++) { + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + tm.acknowledge(); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer2.receiveNoWait(); + assertNull(tm); + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + + ctx.commit(); + } finally { + ctx.close(); + } + } + + @Test + public void testExclusiveQueueConsumerSettingUsingAddressQueueParameters() throws Exception { + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Queue queue = session.createQueue("random?exclusive=true"); + assertEquals("random", queue.getQueueName()); + + ActiveMQDestination a = (ActiveMQDestination) queue; + assertTrue(a.getQueueAttributes().getExclusive()); + + MessageProducer producer = session.createProducer(queue); + + MessageConsumer consumer1 = session.createConsumer(queue); + MessageConsumer consumer2 = session.createConsumer(queue); + MessageConsumer consumer3 = session.createConsumer(queue); + + connection.start(); + + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + + producer.send(message); + } + + + //All msgs should go to the first consumer + for (int j = 0; j < 100; j++) { + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer2.receiveNoWait(); + assertNull(tm); + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + } finally { + connection.close(); + } + } + + @Test + public void testExclusiveQueueConsumerFailover() throws Exception { + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Destination queue = session.createQueue(queueName.toString()); + MessageProducer producer = session.createProducer(queue); + + MessageConsumer consumer1 = session.createConsumer(queue); + MessageConsumer consumer2 = session.createConsumer(queue); + MessageConsumer consumer3 = session.createConsumer(queue); + + connection.start(); + + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + + producer.send(message); + } + + + //All msgs should go to the first consumer + for (int j = 0; j < 50; j++) { + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer2.receiveNoWait(); + assertNull(tm); + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + consumer1.close(); + + //All msgs should now go to the next consumer only, without any errors or exceptions + for (int j = 50; j < 100; j++) { + TextMessage tm = (TextMessage) consumer2.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + } finally { + connection.close(); + } + } + + + @Test + public void testExclusiveTopicSharedConsumerFailover() throws Exception { + ConnectionFactory fact = getCF(); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Destination topic = session.createTopic("mytopic"); + MessageProducer producer = session.createProducer(topic); + + String subscriptionName = "sharedsub"; + Topic topicConsumer = session.createTopic("mytopic?exclusive=true"); + MessageConsumer consumer1 = session.createSharedDurableConsumer(topicConsumer, subscriptionName); + MessageConsumer consumer2 = session.createSharedDurableConsumer(topicConsumer, subscriptionName); + MessageConsumer consumer3 = session.createSharedDurableConsumer(topicConsumer, subscriptionName); + + connection.start(); + + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + + producer.send(message); + } + + + //All msgs should go to the first consumer + for (int j = 0; j < 50; j++) { + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer2.receiveNoWait(); + assertNull(tm); + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + consumer1.close(); + + //All msgs should now go to the next consumer only, without any errors or exceptions + for (int j = 50; j < 100; j++) { + TextMessage tm = (TextMessage) consumer2.receive(10000); + + assertNotNull(tm); + + assertEquals("Message" + j, tm.getText()); + + tm = (TextMessage) consumer3.receiveNoWait(); + assertNull(tm); + } + + + } finally { + connection.close(); + } + } + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/LVQTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/LVQTest.java new file mode 100644 index 0000000000..af4a20ae00 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/LVQTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.artemis.tests.integration.jms.client; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.jms.client.ActiveMQDestination; +import org.apache.activemq.artemis.tests.util.JMSTestBase; +import org.junit.Before; +import org.junit.Test; + +/** + * LVQ Test + */ +public class LVQTest extends JMSTestBase { + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + } + + + protected ConnectionFactory getCF() throws Exception { + return cf; + } + + @Test + public void testLastValueQueueUsingAddressQueueParameters() throws Exception { + ActiveMQConnectionFactory fact = (ActiveMQConnectionFactory) getCF(); + + //Set the consumer window size to 0 to not buffer any messages client side. + fact.setConsumerWindowSize(0); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Queue queue = session.createQueue("random?last-value=true"); + assertEquals("random", queue.getQueueName()); + + ActiveMQDestination a = (ActiveMQDestination) queue; + assertTrue(a.getQueueAttributes().getLastValue()); + + MessageProducer producer = session.createProducer(queue); + MessageConsumer consumer1 = session.createConsumer(queue); + + connection.start(); + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + message.setStringProperty(Message.HDR_LAST_VALUE_NAME.toString(), "key"); + producer.send(message); + } + + //Last message only should go to the consumer + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message99", tm.getText()); + + } finally { + connection.close(); + } + } + + @Test + public void testLastValueQueueTopicConsumerUsingAddressQueueParameters() throws Exception { + ActiveMQConnectionFactory fact = (ActiveMQConnectionFactory) getCF(); + + //Set the consumer window size to 0 to not buffer any messages client side. + fact.setConsumerWindowSize(0); + Connection connection = fact.createConnection(); + + try { + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + Topic topic = session.createTopic("topic?last-value=true"); + assertEquals("topic", topic.getTopicName()); + + ActiveMQDestination a = (ActiveMQDestination) topic; + assertTrue(a.getQueueAttributes().getLastValue()); + + MessageProducer producer = session.createProducer(topic); + MessageConsumer consumer1 = session.createConsumer(topic); + MessageConsumer consumer2 = session.createConsumer(topic); + + connection.start(); + for (int j = 0; j < 100; j++) { + TextMessage message = session.createTextMessage(); + + message.setText("Message" + j); + message.setStringProperty(Message.HDR_LAST_VALUE_NAME.toString(), "key"); + producer.send(message); + } + + + + //Last message only should go to the consumer. + TextMessage tm = (TextMessage) consumer1.receive(10000); + + assertNotNull(tm); + + assertEquals("Message99", tm.getText()); + + //Last message only should go to the other consumer as well. + TextMessage tm2 = (TextMessage) consumer2.receive(10000); + + assertNotNull(tm2); + + assertEquals("Message99", tm2.getText()); + + } finally { + connection.close(); + } + } + +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java index 9351fee685..16d64fd7cf 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java @@ -135,6 +135,16 @@ public class ActiveMQServerControlUsingCoreTest extends ActiveMQServerControlTes return (String) proxy.invokeOperation("updateQueue", name, routingType, maxConsumers, purgeOnNoConsumers); } + @Override + public String updateQueue(@Parameter(name = "name", desc = "Name of the queue") String name, + @Parameter(name = "routingType", desc = "The routing type used for this address, MULTICAST or ANYCAST") String routingType, + @Parameter(name = "maxConsumers", desc = "The maximum number of consumers allowed on this queue at any one time") Integer maxConsumers, + @Parameter(name = "purgeOnNoConsumers", desc = "Delete this queue when the last consumer disconnects") Boolean purgeOnNoConsumers, + @Parameter(name = "exclusive", desc = "If the queue should route exclusively to one consumer") Boolean exclusive) + throws Exception { + return (String) proxy.invokeOperation("updateQueue", name, routingType, maxConsumers, purgeOnNoConsumers, exclusive); + } + @Override public void deleteAddress(@Parameter(name = "name", desc = "The name of the address") String name) throws Exception { proxy.invokeOperation("deleteAddress", name); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java index 82c7a472cc..09621afb92 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/QueueControlUsingCoreTest.java @@ -101,6 +101,16 @@ public class QueueControlUsingCoreTest extends QueueControlTest { return (Boolean) proxy.retrieveAttributeValue("purgeOnNoConsumers"); } + @Override + public boolean isExclusive() { + return (Boolean) proxy.retrieveAttributeValue("exclusive"); + } + + @Override + public boolean isLastValue() { + return (Boolean) proxy.retrieveAttributeValue("lastValue"); + } + @Override public int getDeliveringCount() { return (Integer) proxy.retrieveAttributeValue("deliveringCount", Integer.class); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQRecoveryTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQRecoveryTest.java index f121cbe4db..96c1fd249f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQRecoveryTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQRecoveryTest.java @@ -170,7 +170,7 @@ public class LVQRecoveryTest extends ActiveMQTestBase { server = createServer(true, configuration); server.start(); - qs = new AddressSettings().setLastValueQueue(true); + qs = new AddressSettings().setDefaultLastValueQueue(true); server.getAddressSettingsRepository().addMatch(address.toString(), qs); // then we create a client as normal locator = createInVMNonHALocator().setBlockOnAcknowledge(true).setAckBatchSize(0); @@ -188,7 +188,7 @@ public class LVQRecoveryTest extends ActiveMQTestBase { // start the server server.start(); - server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setLastValueQueue(true)); + server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setDefaultLastValueQueue(true)); // then we create a client as normal locator.close(); locator = createInVMNonHALocator().setBlockOnAcknowledge(true).setAckBatchSize(0); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQTest.java index 0706bc00ce..f262aede85 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/LVQTest.java @@ -648,7 +648,7 @@ public class LVQTest extends ActiveMQTestBase { // start the server server.start(); - server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setLastValueQueue(true)); + server.getAddressSettingsRepository().addMatch(address.toString(), new AddressSettings().setDefaultLastValueQueue(true)); // then we create a client as normalServer ServerLocator locator = createNettyNonHALocator().setBlockOnAcknowledge(true).setAckBatchSize(0); diff --git a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/message/MessageHeaderTest.java b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/message/MessageHeaderTest.java index 6e1fff2767..f249383c66 100644 --- a/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/message/MessageHeaderTest.java +++ b/tests/jms-tests/src/test/java/org/apache/activemq/artemis/jms/tests/message/MessageHeaderTest.java @@ -917,6 +917,12 @@ public class MessageHeaderTest extends MessageHeaderTestBase { } + @Override + public void createSharedQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + boolean durable, Integer maxConsumers, Boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + + } + /** * Creates a non-temporary queue. * @@ -1018,6 +1024,13 @@ public class MessageHeaderTest extends MessageHeaderTestBase { } + @Override + public void createQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, boolean durable, + boolean autoCreated, int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) + throws ActiveMQException { + + } + /** * Creates a non-temporaryqueue. * @@ -1048,6 +1061,13 @@ public class MessageHeaderTest extends MessageHeaderTestBase { } + @Override + public void createQueue(String address, RoutingType routingType, String queueName, String filter, boolean durable, + boolean autoCreated, + int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) throws ActiveMQException { + + } + /** * Creates a temporary queue. * @@ -1074,6 +1094,13 @@ public class MessageHeaderTest extends MessageHeaderTestBase { } + @Override + public void createTemporaryQueue(SimpleString address, RoutingType routingType, SimpleString queueName, SimpleString filter, + int maxConsumers, boolean purgeOnNoConsumers, Boolean exclusive, Boolean lastValue) + throws ActiveMQException { + + } + /** * Creates a temporary queue with a filter. * diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java index f0afd9e0fa..6e66057f49 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/postoffice/impl/FakeQueue.java @@ -47,6 +47,23 @@ public class FakeQueue extends CriticalComponentImpl implements Queue { } + @Override + public boolean isExclusive() { + // no-op + return false; + } + + @Override + public void setExclusive(boolean value) { + // no-op + } + + @Override + public boolean isLastValue() { + // no-op + return false; + } + @Override public void setMaxConsumer(int maxConsumers) { diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java index 98a7359efe..96c7451f92 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/server/impl/fakes/FakePostOffice.java @@ -46,7 +46,8 @@ public class FakePostOffice implements PostOffice { public QueueBinding updateQueue(SimpleString name, RoutingType routingType, Integer maxConsumers, - Boolean purgeOnNoConsumers) throws Exception { + Boolean purgeOnNoConsumers, + Boolean exclusive) throws Exception { return null; }